<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-20788959</id><updated>2011-12-13T22:55:54.079-05:00</updated><title type='text'>Best Application Performance</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>14</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-20788959.post-114321217727525493</id><published>2006-03-24T09:54:00.000-05:00</published><updated>2006-03-24T09:56:17.286-05:00</updated><title type='text'>Consideraciones en los WebServices: WebMethods</title><content type='html'>Sabemos que cuando a los métodos públicos de un Web Service le colocamos el atributo WebMethod, lo estamos exponiendo para llamadas remotas.  Para esto también es necesario tener en cuenta algunas cosillas:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Parámetros con tipos nativos.&lt;/span&gt;  Con esto minimizamos la serialización, dado que se hace una validación automática gracias al .NET Framework.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Buffering.&lt;/span&gt; Por defecto, la configuración de buffer (BufferResponse) está fijada en verdadero, para asegurar que la respuesta estará en el buffer completamente antes de pasar al cliente.  Esto es bueno cuando se retorna pequeños datos, pero obviamente, en grandes cantidades, es preferible deshabilitar el buffering.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;[WebMethod(BufferResponse=false)]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;Public string GetTextFile() &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Almacenar los datos en caché.&lt;/span&gt;  Siempre y cuando los datos a retornar no sean muy volátiles.  Se puede especificar el tiempo –en segundos- que la respuesta estará almacenada en caché.  Pero ojo, dado que el caché consume memoria del servidor, no sería apropiado si el WebMethod devuelve mucha información.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;[WebMethod(CacheDuration=60)]&lt;br /&gt;Public String GetDetails()&lt;br /&gt;{&lt;br /&gt;...&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-114321217727525493?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/114321217727525493/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=114321217727525493&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/114321217727525493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/114321217727525493'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/2006/03/consideraciones-en-los-webservices.html' title='Consideraciones en los WebServices: WebMethods'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20788959.post-114072178652662049</id><published>2006-02-23T14:08:00.000-05:00</published><updated>2006-02-23T14:09:46.546-05:00</updated><title type='text'>Mejorando el rendimiento de los DataSets</title><content type='html'>Usualmente cuando necesitamos trabajar con datos de una manera desconectada, nos valemos de los DataSets, llenados por un DataAdapter.  Podemos seguir las siguientes recomendaciones para mejorar el rendimiento o performance de un DataSet.&lt;br /&gt;&lt;br /&gt;La serialización de los DataSet fue mucho mejor implementada en el .NET Framework 1.1; sin embargo, conduce a cuellos de botella.  Para evitar esos inconvenientes, tenemos varias alternativas:&lt;br /&gt;&lt;br /&gt;Utilizar alias de columnas.  Los datos serializados contienen los nombres de las columnas de manera que lo podemos utilizar para minimizar la serialización.  De igual manera, si no vamos a necesitar los datos que se vayan modificando, se puede llamar al método AcceptChanges antes de serializar el DataSet para limpiar el buffer interno.  Esto debido a que el DataSet mantiene historia de los datos originales.&lt;br /&gt;&lt;br /&gt;Para las búsquedas, utilicemos mejor las claves primarias y Rows.Find; en lugar de DataTable.Select.  La sentencia DataTable.Select no utiliza índices.  Ya si nos toca efectuar búsquedas repetitivas sobre campos no indexados, es preferible utilizar un dataview, ya que se crea un índice automáticamente, lo que ayudará a mejorar el rendimiento de la aplicación.&lt;br /&gt;&lt;br /&gt;En el caso anterior, si nos vamos por dicha estrategia, debemos utilizar el constructor del DataView que recibe como parámetros el Sort, RowFilter y RowStateFilter; de manera que nos aseguramos que el índice se crea una sola vez.  En caso que creemos el dataset y luego vamos seteando las propiedades, el índice se crea por lo menos dos veces.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-114072178652662049?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/114072178652662049/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=114072178652662049&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/114072178652662049'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/114072178652662049'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/2006/02/mejorando-el-rendimiento-de-los.html' title='Mejorando el rendimiento de los DataSets'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20788959.post-114011494567770597</id><published>2006-02-16T13:33:00.000-05:00</published><updated>2006-02-16T13:35:45.686-05:00</updated><title type='text'>Otros tips para manejo de índices en SQL Server</title><content type='html'>Por lo general abusamos de la creación de los índices en el SQL Server.  Para poder tener mejor manejo de los índices, tenemos algunos tips adicionales.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Es mejor crear los índices basados en el uso que se le van a dar.&lt;/strong&gt;  Muchas veces resulta muy tentador crear una tabla y su índice a renglón seguido, por lo general con el secuencial de por medio.  O tal vez al momento que vemos una consulta muy lenta, le creamos un índice y punto.  NO.  Los índices pueden afectar negativamente negativamente a las operaciones de escritura.  Lo mejor siempre aquí es tener conocimiento del sistema que estamos trabajando, la carga que va a tener, las consultas más conflictivas.  Crear índices no es un arte, es fruto de la experiencia con el sistema.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Los índices “clustered” deben mantenerse pequeños.&lt;/strong&gt;  Esto debido a que los índices “non-clustered” almacenan la clave del índice clustered para ubicar las filas.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;En los índices “clustered”, consideremos los rangos de datos.&lt;/strong&gt;  Si frecuentemente utilizamos comparativos de rangos en nuestras consultas, mediante operadores between o &lt; ó &gt;, sería bueno tener un índice clustered sobre el campo consultado.  Es más, podríamos decir que todas las tablas deberían tener su índice clustered a menos que hayamos demostrado que afecta al performance.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Índices compuestos.&lt;/strong&gt;  Cuando creamos un índice compuesto –es decir, con varias columnas-, únicamente la primera almacena las estadísticas.  Esto quiere decir que dicha columna deberá ser la más restrictiva.  Si a pesar de ello el índice no es completamente selectivo, el motor no lo utilizará.  Si alguna sentencia WHERE no utiliza todas las columnas del índice compuesto, es posible que el motor no utilice el índice.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Eliminar índices no utilizados.&lt;/strong&gt;  Como ya indiqué, las operaciones de escritura son afectadas por los índices.  Tener índices de más, ocasionará que el sistema no responda de manera efectiva.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Index Tuning Wizard.&lt;/strong&gt;  Es una buena manera de analizar los índices, pero a mi en lo particular me gusta más basarme en la experiencia propia.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-114011494567770597?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/114011494567770597/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=114011494567770597&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/114011494567770597'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/114011494567770597'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/2006/02/otros-tips-para-manejo-de-ndices-en.html' title='Otros tips para manejo de índices en SQL Server'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20788959.post-114004401810103355</id><published>2006-02-15T17:46:00.000-05:00</published><updated>2006-02-15T17:53:38.120-05:00</updated><title type='text'>Common Table Expressions</title><content type='html'>Una de las ventajas que nos trae SQL Server 2005 es el uso de expresiones de tablas comunes, o common table expressions.  Tratando de interpretarlo, es como un conjunto de datos resultante temporal.  Es muy útil en momento de reemplazar subqueries o tablas derivadas.&lt;br /&gt;&lt;br /&gt;En el siguiente ejemplo, vemos una CTE llamada mid, que calcula la mediana de un conjunto de datos.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;WITH mid AS&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;(SELECT ((MAX(value) - MIN(value)) / 2) &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; AS midval FROM invoices)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;SELECT  &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;CASE  WHEN value &gt; mid.midval THEN 0  &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;ELSE 1 END AS half, invoices.*   &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;FROM invoices, mid ORDER BY half&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Aquí podremos ver entonces de qué se componen los CTE.  Empiezan con una cláusula WITH, y la expresión que vamos a tener la colocamos entre paréntesis.  En caso que tengamos múltiples CTE's, las separamos por comas.  Luego colocamos la sentencia SELECT.&lt;br /&gt;&lt;br /&gt;La gran ventaja de esto es que si es utilizada más de una vez, nos provee muchos menos lecturas que un subquery.  Por ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;WITH low AS (SELECT ((max(amount)) / 3)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  AS v FROM invoices),&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;high AS (SELECT (2 * max(amount) / 3)   AS v FROM invoices)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;select id, amount, amount - low.v  &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;FROM invoices, low, high  &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;WHERE invoices.amount &gt; low.v  &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;AND invoices.amount &lt;= high.v&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;SELECT id, amount, &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;amount - (SELECT (max(amount) / 3) FROM invoices) &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;FROM invoices where &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;amount &gt; (SELECT (max(amount) / 3) FROM invoices) and &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;amount &lt; (SELECT (2 * max(amount) / 3) FROM invoices)&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Si comparamos los planes de ejecución, notaremos una mejora gracias a la CTE.&lt;br /&gt;&lt;br /&gt;De la misma manera, nos ahorran grandes procedimientos.  Por ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;WITH low AS (SELECT ((MAX(amount)) / 3)  &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;AS v FROM invoices),&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;high AS (SELECT (2 * MAX(amount) / 3)  &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;AS v FROM invoices)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;SELECT id, amount, amount - low.v  &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;FROM invoices, low, high  &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;WHERE invoices.amount &gt; low.v  &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;AND invoices.amount &lt;= high.v&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En lugar de&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;declare @high int&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;declare @low int&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;SELECT @high = max(amount) FROM invoices&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;SELECT @low = min(amount) FROM invoices&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;SELECT id, amount, amount - @low FROM invoices&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;El resultado final será el mismo, pero la desventaja es que no puede ser utilizada en una vista; cosa que el CTE si puede hacer.  En palabras algo más técnicas, la CTE se vuelve declarativa, mientras que el query equivalente es un procedimiento.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-114004401810103355?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/114004401810103355/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=114004401810103355&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/114004401810103355'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/114004401810103355'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/2006/02/common-table-expressions.html' title='Common Table Expressions'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20788959.post-113882214926105251</id><published>2006-02-01T14:27:00.000-05:00</published><updated>2006-02-01T14:29:09.293-05:00</updated><title type='text'>Densidad de los índices</title><content type='html'>Usualmente cuando creamos índices, pensamos siempre en aquellos que satisfacen las condiciones de la cláusula WHERE en las consultas más utilizadas.  Esto no es una manera incorrecta de pensar, de hecho, es lo mejor que se puede hacer.  Pero no es todo.&lt;br /&gt;&lt;br /&gt;A parte de crear el índice de esa manera, debemos de pensar en un índice que sea altamente selectivo.  ¿Qué es eso?  Un índice altamente selectivo tiene gran cantidad de valores distintos.  Por ejemplo, un índice en una tabla de clientes, sobre un campo “flag”, tendrá solamente 2 valores distintos (sí/no).  Sin embargo, un índice por el número de identificación, puede tener muchísimos.  En el primer caso, es muy probable que el motor del SQL Server no lo utilice, dado que no es muy selectivo.&lt;br /&gt;&lt;br /&gt;Afortunadamente, se tiene el comando DBCC SHOW_STATISTICS que permitirá entender la selectividad de un índice.  Como salida de este comando, tenemos una columna Density, la misma que se calcula dividiendo 1 para el número de valores distintos.  De esta manera, un índice único tendrá una densidad de 1/número de filas.  Así, una tabla de 1000 filas tendrá una densidad de 0.001.  En el caso anterior, el índice sobre la columna “flag”, tendrá una densidad de 0.5.  Entonces, mientras más pequeño sea el número, mayor será la selectividad.&lt;br /&gt;&lt;br /&gt;El mejor número será el que devuelve la columna All Density.&lt;br /&gt;&lt;br /&gt;Más información en:  &lt;a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsql2k/html/statquery.asp"&gt;Statistics Used by the Query Optimizer in SQL Server 2000&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-113882214926105251?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/113882214926105251/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=113882214926105251&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113882214926105251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113882214926105251'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/2006/02/densidad-de-los-ndices.html' title='Densidad de los índices'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20788959.post-113837364643266401</id><published>2006-01-27T09:52:00.000-05:00</published><updated>2006-01-27T09:54:06.440-05:00</updated><title type='text'>Transacciones: todo un dolor de cabeza</title><content type='html'>&lt;span style="font-size:78%;"&gt;Este artículo aplica a SQL Server 2000.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Si de verdad te interesa mejorar enormemente el rendimiento de tu aplicación, es importante que te enfoques en manejar eficientemente las transacciones.  Las transacciones bloquean recursos de manera que pueden bloquear otras transacciones.  Algunos trucos efectivos al momento de manejar transacciones los voy a listar a continuación, esperando que les sean lo más útil posible.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Evitar transacciones de larga duración.&lt;/strong&gt;  Dado que las transacciones efectúan bloqueos, lo más barato es mantener las transacciones lo más cortas posibles.  Igual, se puede iniciar transacciones en la capa de aplicación.  Una técnica bastante aceptada –y que me encanta utilizar- es realizar todas las validaciones previas, antes de iniciar la transacción.  Quizá debas volverlas a verificar durante la transacción, pero la ventaja es que se puede evitar que por una de esas condiciones tengas que hacer un rollback en media transacción.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Nunca, jamás, utilizar transacciones que requieran de ingresos del usuario.&lt;/strong&gt;  Nada más que agregar.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Los datos más utilizados deberán ser accedidos al final de la transacción.&lt;/strong&gt;  Es mejor que todas las operaciones de lectura, sean colocadas al inicio de la transacción; las escrituras al final; y los recursos más accedidos al último.  ¿Adivinaste?  La idea es que los bloqueos son más cortos en los recursos más accedidos.  Adiós a los bloqueos largos.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Acceder a los recursos en el mismo orden.&lt;/strong&gt;  Los deadlocks son las cosas mas molestas en la base de datos.  Para evitarlos, debemos de tratar de utilizar los recursos en el mismo orden, en todo nuestro sistema.  Si no lo hacemos corremos alto riesgo de enfrentarnos a escenarios de deadlock bastante frecuentes.  Conseguir un sistema sin deadlocks es una tarea casi imposible; pero el objetivo deberá ser tratar de llevarlos al mínimo, reduciendo el tiempo en el que los bloqueos son efectuados.  Esto incluso podríamos tratarlo en el futuro.  Hay bastante que aprender acá.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Utilizar los hints de aislamiento (isolation).&lt;/strong&gt;  Si la lógica del negocio lo permite, podríamos llevar el nivel de aislamiento más bajo.  Lo más común es utilizar el hint WITH NOLOCK en las sentencias select.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Las transacciones explícitas deberán de hacer commit o rollback.&lt;/strong&gt;  Se debe tratar que exista el manejo de errores adecuado, de manera que se hagan el commit o el rollback necesario.  Sino jamás se cerrará la transacción.  Si se tienen sospechas de alguna transacción abierta, se puede utilizar la sentencia DBCC OPENTRAN, que devolverá las transacciones abiertas.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-113837364643266401?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/113837364643266401/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=113837364643266401&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113837364643266401'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113837364643266401'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/2006/01/transacciones-todo-un-dolor-de-cabeza.html' title='Transacciones: todo un dolor de cabeza'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20788959.post-113803909478559435</id><published>2006-01-23T12:54:00.000-05:00</published><updated>2006-01-23T13:06:13.236-05:00</updated><title type='text'>Sobre la administración de excepciones (2)</title><content type='html'>¿Sabías que puedes monitorear la cantidad de excepciones que tu aplicación está lanzando? Se puede hacer de dos maneras sencillas:&lt;br /&gt;&lt;br /&gt;- Monitoreando periódicamente el log de excepciones, siempre que se haya implementado código para manejo de excepciones (similar a lo indicado en la entrada anterior).&lt;br /&gt;- Con el Performance Monitor de Windows, se puede ver el valor del contador # of Exceps Thrown / sec que se encuentra en el objeto .NET CLR Exceptions. Digamos que para indicar un manejo adecuado, este valor deberá ser menor que el 5% de tus solicitudes por segundo.&lt;br /&gt;&lt;br /&gt;Otra manera de evitar que las excepciones afecten al rendimiento de una aplicación, es hacerse cargo de aquellos recursos que son tomados y que al ocurrir una excepción podrían eventualmente quedar en memoria. Para esto pues utilizamos la cláusula finally. Con esto no aseguraremos que los recursos son liberados una vez ocurrida la excepción. De la siguiente manera:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;try&lt;br /&gt;{&lt;br /&gt;conn.Open();&lt;br /&gt;…&lt;br /&gt;}&lt;br /&gt;finally&lt;br /&gt;{&lt;br /&gt;If (null !=conn) conn.Close();&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Y lo más importante, escribir código que evite las excepciones. Por ejemplo.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Verificar valores nulos.&lt;/strong&gt; En caso de ser posible que un objeto tenga el valor null, habría que asegurarse que no es nulo, en lugar de lanzar una excepción. Esto comúnmente sucede cuando se tratan de consultar ítems del session state, view state, application state, u objetos del cache. Por ejemplo, NO conviene utilizar el siguiente código para acceder información a un objeto de sesión:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;try{&lt;br /&gt;loginid = Session[“loginid”].ToString();&lt;br /&gt;}&lt;br /&gt;catch (Exception ex){&lt;br /&gt;Response.Redirect(“login.aspx”,false);&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En su lugar, bien podríamos utilizar&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;if (Session[“loginid”]!=null)&lt;br /&gt;loginid = Session[“loginid”].ToString();&lt;br /&gt;else&lt;br /&gt;Response.Redirect(“login.aspx”,false);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;No utilizar las excepciones para controlar lógica.&lt;/strong&gt; Las excepciones son eso – excepciones. Una falla en la conexión a una base de datos es una excepción. Un usuario que se equivoca en digitar el password es una mera condición que necesita ser manejada. Por ejemplo, el siguiente código:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;public void Login(string UserName, string Password) {}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;El siguiente código se utiliza para llamar al login.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;try&lt;br /&gt;{&lt;br /&gt;Login(userName, password);&lt;br /&gt;}&lt;br /&gt;catch (InvalidUserNameException ex)&lt;br /&gt;{…}&lt;br /&gt;catch (InvalidPasswordException ex)&lt;br /&gt;{…}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Es mucho mejor crear un enumerado de los posibles valores y cambiar el método login para que retorne la enumeración; algo así:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;public enum LoginResult&lt;br /&gt;{&lt;br /&gt;Success, InvalidUserName, InvalidPassword, AccountLockedOut&lt;br /&gt;}&lt;br /&gt;public LoginResult Login(string UserName, string Password) {}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Y utilizar el siguiente código para llamar a Login:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;LoginResult result = Login(userName, password);&lt;br /&gt;switch(result)&lt;br /&gt;{&lt;br /&gt;case Success: …&lt;br /&gt;case InvalidUserName: …&lt;br /&gt;case InvalidPassword: …&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Suprimir las llamadas internas a Response.End.&lt;/strong&gt; Los métodos Server.Transfer, Response.Redirect y Response.End, levantan excepciones. Cada uno de ellos llama internamente al método Response.End. La llamada a Response.End, provoca una excepción ThreadAbortException. Si utilizas Response.Redirect, mejor utiliza el método recargado y pasa false al segundo parámetro para evitar la llamada interna a Response.End. Más información en: &lt;a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;312629"&gt;PRB: ThreadAbortException Occurs If You Use Response.End, Response.Redirect, or Server.Transfer&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;No atrapar excepciones que no estamos manejando.&lt;/strong&gt; Si el código no puede manejar una excepción, utiliza un bloque try/finally para asegurar que se cierran los recursos, aún si la excepción ocurre o no. No atrapes la excepción si no se intenta recuperar; antes bien, permítele propagarse para un manejador adecuado que pueda “lidiar” con la excepción encontrada.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-113803909478559435?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/113803909478559435/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=113803909478559435&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113803909478559435'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113803909478559435'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/2006/01/sobre-la-administracin-de-excepciones_23.html' title='Sobre la administración de excepciones (2)'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20788959.post-113769592844660380</id><published>2006-01-19T13:35:00.000-05:00</published><updated>2006-01-19T13:38:48.453-05:00</updated><title type='text'>Sobre la administración de excepciones</title><content type='html'>(&lt;span style="font-size:78%;"&gt;Este artículo aplica para Visual Studio 2003).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Las excepciones son muy costosas. Al conocer las causas de la excepción, y escribir código a prueba de las mismas o que las maneje de manera eficiente, se puede incrementar drásticamente el rendimiento (performance) y escalabilidad de tu aplicación. Cuando se diseña e implementa manejo de excepciones, hay que analizar varios factores para asegurar un rendimiento óptimo. Así, por ejemplo, podríamos tener:&lt;br /&gt;&lt;br /&gt;Implementar un administrador de errores en el Global.asax&lt;br /&gt;&lt;br /&gt;El primer paso para administrar las excepciones es implementar un administrador global de errores en el archivo Global.asax o en el code-behind del mismo. Esto permite atrapar todas las excepciones que no hayan sido manejadas en la aplicación. Dentro del administrador por lo menos deberíamos preocuparnos de registrar la página que ocasiona el error, el información de la pila (call stack) y el nombre de la excepción y el mensaje que arroja.&lt;br /&gt;&lt;br /&gt;Esto es muy sencillo de programar, en el evento Application_Error, de la siguiente manera:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;public void Application_Error(object s, EventArgs ev)&lt;br /&gt;{&lt;br /&gt;StringBuilder message = new StringBuilder();&lt;br /&gt;If (Server != null) {&lt;br /&gt;Exception e;&lt;br /&gt;For (e = Server.GetLastError(); e != null; e = e.InnerException)&lt;br /&gt;{&lt;br /&gt;Message.AppendFormat(“{0}: {1}{2}”,&lt;br /&gt;e.GetType().FullName,&lt;br /&gt;e.Message,&lt;br /&gt;e.StackTrace);&lt;br /&gt;}&lt;br /&gt;// Registrar en el log la información&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;(La traducción a VB.NET se las dejo a ustedes).&lt;br /&gt;&lt;br /&gt;Más información:&lt;br /&gt;&lt;br /&gt;MSDN: &lt;a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/customerrors.asp"&gt;Rich Custom Error Handling with ASP.NET&lt;/a&gt;&lt;br /&gt;HOW TO: &lt;a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;306355"&gt;Create Custom Error Reporting Pages in ASP.NET by Using Visual C# .NET&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Continuará…&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-113769592844660380?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/113769592844660380/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=113769592844660380&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113769592844660380'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113769592844660380'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/2006/01/sobre-la-administracin-de-excepciones.html' title='Sobre la administración de excepciones'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20788959.post-113752324086542703</id><published>2006-01-17T13:39:00.000-05:00</published><updated>2006-01-17T13:40:40.873-05:00</updated><title type='text'>Como reducir round trips</title><content type='html'>En la entrada anterior veíamos que una de las causas comunes de degradación del performance es el uso de muchos round trips en las páginas ASP.NET.  Utilizando los siguientes tips, podríamos reducir su uso –y abuso-.&lt;br /&gt;&lt;br /&gt;En medida de lo posible, agrupemos las sentencias SQL.  Fallas en este sentido crean viajes adicionales –e innecesarios- a la base de datos.  Pueden agruparse sentencias SQL separándolas con un punto y coma (;) o utilizando un stored procedure que ya lo haga.  Los resultados se los puede ir leyendo con un objeto DataReader, con el método NextResult.&lt;br /&gt;&lt;br /&gt;Para evitar round trips, se puede utilizar “connection pooling”.  Al reutilizar las conexiones desde un pool, se evita los round trips que son necesarios debido al establecimiento de la conexión como tal y la autenticación.  En esto podríamos ahondar un poco más en el futuro.&lt;br /&gt;&lt;br /&gt;No retornar datos innecesarios.  No me cansaré de repetirlo.  Es absurdo.  Si necesitamos un dato sencillo, podríamos utilizar el método ExecuteScalar.  De igual manera se puede utilizar la sentencia ExecuteNonQuery, si queremos ejecutar sentencias de definición de datos (DDL), como la sentencia CREATE TABLE.  Esto ayuda también a reducir el costo de crear el conjunto de resultados.&lt;br /&gt;&lt;br /&gt;Se puede utilizar el caché (caching) para mantener los datos cerca del cliente en lugar de ir a la base en cada round trip.&lt;br /&gt;&lt;br /&gt;Hay que tener cuidado de los round trips implícitos.  Estos se producen en las operaciones que extraen metadata de la base de datos.  Por ejemplo, deberíamos evitar el método DeriveParameters si ya conocemos información sobre los parámetros.  Es mucho mejor –y eficiente- llenar la colección de parámetros explícitamente.  La siguiente línea de ejemplo muestra una llamada que causa un round trip explícito.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;SqlCommandBuilder.DeriveParameters(cmd)&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-113752324086542703?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/113752324086542703/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=113752324086542703&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113752324086542703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113752324086542703'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/2006/01/como-reducir-round-trips.html' title='Como reducir round trips'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20788959.post-113743797084006190</id><published>2006-01-16T13:56:00.000-05:00</published><updated>2006-01-16T13:59:30.850-05:00</updated><title type='text'>Problemas de rendimiento y escalabilidad (¿No se les hacen conocidos?)</title><content type='html'>La siguiente es una lista de los problemas más comunes (inclusive más comunes de lo que pareciesen) que afectan enormemente al rendimiento y escalabilidad de la capa de datos en una aplicación.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;-Queries ineficientes.&lt;/strong&gt;  Los queries que procesan y retornan más filas o columnas de lo necesario desperdician recursos del sistema que bien pueden ser aprovechados para servir otras peticiones.  Peor aún si estos queries no toman ventajas de los índices existentes.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;-Traer mucha información.&lt;/strong&gt;  Mucha información o data en los resultados, la mayoría de las ocasiones, es producida por consultas ineficientes.  Un query con sentencia Select * usualmente provoca este problema.  También vale la pena analizar la sentencia WHERE en las consultas para verificar que no se retornan muchas filas.  Siempre se debe tratar de hacer la sentencia WHERE lo más específico posible.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;-Indices ineficientes o no existentes.&lt;/strong&gt;  Los queries se dañan o degradan ante la inexistencia de índices dado que un “barrido” de la tabla (full table scan) debe ser ejecutado.  Así, a medida que los datos van creciendo (como en todo sistema), las tablas pueden resultar fragmentadas.  Aquí lo recomendables es tener un plan de reconstrucción de los índices, para evitar este escenario.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;-“Round trips” innecesarios.&lt;/strong&gt;  No hay mucho que agregar.  Un “round trip” siempre afecta al rendimiento o performance de la aplicación.  Está pegado además al tráfico de la red y carga del servidor.  Hay que tratar de mantener a los “round trips” lo mínimo posible.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;-Muchas conexiones abiertas.&lt;/strong&gt;  Las conexiones son de por sí un recurso costoso, que debe ser compartido entre varios procesos utilizando “connection pooling”.  Abrir una conexión por cada proceso limita terriblemente la escalabilidad.  Evitemos tener conexiones abiertas y cadenas de conexión variantes.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;-Liberación de recursos.&lt;/strong&gt;  Un problema común también resulta en la falta de liberación de recursos, lo que previene que sean reutilizados.  Si no se cierran las conexiones antes que la conexión caiga fuera del alcance de la misma, no se libera mientras el objeto no se elimina de la memoria.  Esto puede provocar presión sobre los recursos, lo que obviamente nos conduce a un problema.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;-Abuso de transacciones.&lt;/strong&gt;  Si se escoge el tipo incorrecto de transacción, se puede añadir mucha latencia a cada operación.  De igual manera, si mantenemos las transacciones abiertas por periodos largos de tiempo, resultará contraproducente.  Si bien es cierto las transacciones son necesarias para asegurar la integridad de los datos, necesitas asegurarte que el tipo correcto de transacción está siendo utilizado.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;-Mucha normalización.&lt;/strong&gt;  Esto puede provocar muchos joins entre las tablas.  Obviamente afectará al rendimiento.&lt;br /&gt;&lt;br /&gt;¿Cuántos tienes?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-113743797084006190?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/113743797084006190/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=113743797084006190&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113743797084006190'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113743797084006190'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/2006/01/problemas-de-rendimiento-y.html' title='Problemas de rendimiento y escalabilidad (¿No se les hacen conocidos?)'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20788959.post-113709134249744320</id><published>2006-01-12T13:39:00.000-05:00</published><updated>2006-01-12T13:42:22.496-05:00</updated><title type='text'>Tips para optimización de queries en SQL Server (2)</title><content type='html'>Otro tip para optimización de una consulta en SQL Server es evitar el uso de funciones explícitas o implícitas en el predicado de la cláusula WHERE. Las columnas en la cláusula WHERE son vistas como expresiones en lugar de columnas. De hecho, las columnas no son utilizadas en el plan de ejecución del optimizador. Un problema común es que las columnas cuyo tipo de dato es datetime. Si tienes una columna de dicho tipo en una cláusula WHERE, y necesitas convertirla o usar como una función de datos, trata de forzar a interpretarla como una expresión de caracteres.&lt;br /&gt;&lt;br /&gt;El siguiente query, con una función en la columna datetime causa un barrido de tabla en la base de datos Northwind, inclusive con un índice en la columna OrderDate:&lt;br /&gt;&lt;br /&gt;select OrderID from Northwind.dbo.Orders WHERE DATEADD(day, 15, OrderDate) = '07/23/1996'&lt;br /&gt;&lt;br /&gt;Sin embargo, si “arreglamos” un poco el query, moviendo la función al otro lado del WHERE, el índice podrá ser utilizado en la columna datetime. De la siguiente manera:&lt;br /&gt;&lt;br /&gt;select OrderID from Northwind.dbo.Orders WHERE OrderDate = dateadd(day, -15, '07/23/1996')&lt;br /&gt;&lt;br /&gt;A continuación el plan de ejecución gráfico de ambas consultas. En el primer caso, el ícono corresponde a un barrido del índice (Index Scan), mientras que en el segundo corresponde a una búsqueda (Index Seek).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://photos1.blogger.com/blogger/6762/528/1600/Queries_2.jpg"&gt;&lt;img style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://photos1.blogger.com/blogger/6762/528/400/Queries_2.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Una vez más, los números hablan por sí solos.&lt;br /&gt;&lt;br /&gt;Las conversiones implícitas también pueden ser causa de “barridos”, dado a discordancia en los tipos de datos. Mucho ojo con los tipos de dato varchar y nvarchar, así como con los nchar y nvarchar, dado que pueden ejecutar una conversión implícita. Por ejemplo (la columna CustomerId es del tipo nchar):&lt;br /&gt;&lt;br /&gt;declare @CustId char(5)&lt;br /&gt;set @CustId = 'FOLKO'&lt;br /&gt;select CompanyName from Northwind.dbo.Customers where CustomerId = @CustId&lt;br /&gt;&lt;br /&gt;Arroja el siguiente plan de ejecución:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://photos1.blogger.com/blogger/6762/528/1600/Queries_3.jpg"&gt;&lt;img style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://photos1.blogger.com/blogger/6762/528/400/Queries_3.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;No es mucha la diferencia en performance, pero siempre es buena práctica manejar cuidadosamente los mismos tipos de dato.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-113709134249744320?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/113709134249744320/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=113709134249744320&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113709134249744320'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113709134249744320'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/2006/01/tips-para-optimizacin-de-queries-en_12.html' title='Tips para optimización de queries en SQL Server (2)'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20788959.post-113702557040177919</id><published>2006-01-11T19:22:00.000-05:00</published><updated>2006-01-12T13:39:42.216-05:00</updated><title type='text'>Tips para optimización de queries en SQL Server (1)</title><content type='html'>Más que conocer trucos y sintaxis, escribir un buen query (consulta) en SQL Server exige un ejercicio constante en ser capaz de crear queries relacionales “elegantes”. Por lo general estos últimos dan mucho mejores resultados –claro, siempre que la base esté también correctamente relacionada, lo que podríamos ver luego-.&lt;br /&gt;&lt;br /&gt;Pues aquí trataremos dos tips indispensables al momento de escribir consultas o queries en SQL Server. Dejaremos otros tips para el futuro, o si tienen alguno, coméntenlo sin problemas.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Consultar únicamente las filas y columnas requeridas&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Puede sonar bastante obvio, pero a la vez es una estrategia bastante buena al momento de elaborar queries. Es muy común que alguna consulta devuelva o muchas columnas o muchas filas. Si estamos retornando muchas columnas, seguramente nuestro query está abusando del “SELECT * FROM …”. Pues noticia: estas columnas de la sentencia SELECT también son tomadas en cuenta por el motor de la base de datos (concretamente el optimizador), cuando evalua el índice a utilizar en la consulta y armar el plan de ejecución de la misma. A parte de retornar muchas veces datos irrelevantes, también puede forzar barridos de índices (index scan), en lugar de las restricciones que puedan surgir debido a la cláusula WHERE. Esto debido a que el costo de ir al índice agrupado (clustered index) para devolver el resto de datos de la fila después de utilizar un índice no agrupado (nonclustered index) para limitar los resultados, puede resultar más elevado que “barrer” el índice agrupado.&lt;br /&gt;&lt;br /&gt;A continuación un ejemplo.&lt;br /&gt;&lt;br /&gt;select * from northwind.dbo.Orders where OrderDate &lt; '01/01/1996' &lt;br /&gt;select Orderid from northwind.dbo.Orders where OrderDate &lt; '01/01/1996' &lt;br /&gt;&lt;br /&gt;Estos dos queries tienen los siguientes planes de ejecución: &lt;br /&gt;&lt;br /&gt;&lt;a href="http://photos1.blogger.com/blogger/6762/528/1600/Queries.0.jpg"&gt;&lt;img style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://photos1.blogger.com/blogger/6762/528/400/Queries.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;En el primer caso, a pesar de existir un índice en la tabla por el campo OrderDate, notamos que hace un “barrido” del índice agrupado, para poder mostrarlos demás datos. En el segundo caso, como únicamente tiene que mostrar el campo OrderId, y dado que OrderId forma parte del índice agrupado, únicamente realiza una búsqueda sobre el índice OrderDate. Los números hablan por sí solos. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Evitar operadores costosos, como el NOT LIKE.&lt;/strong&gt; &lt;br /&gt;&lt;br /&gt;Algunos operadores, tanto en los joins como en los predicados del WHERE, tienden a producir operaciones muy costosas. El operador LIKE con un valor encerrado entre los comodines (“%&lt;em&gt;valor&lt;/em&gt;%”) casi siempre producen un barrido de la tabla (ni siquiera del índice). Si lo utilizamos con únicamente un lado del comodín, podrá hacer uso del índice, dado que el índice es parte de un árbol B+ (para aquellos que pensaban que estructura de datos en la U no iba a servir, ¡toma!), y el índice es consultado haciendo comparaciones de izquierda a derecha.&lt;br /&gt;&lt;br /&gt;Por Dios, ni se les ocurra utilizar operadores negativos (&lt;&gt;, NOT LIKE) dado que casi nunca el optimizador los resuelve eficientemente. Escriban las consultas de cualquier otra manera. Si por último, están tratando de verificar existencia, utilicen mejor el IF EXISTS o IF NOT EXISTS. Si ya están en la obligación de hacer un barrido, se puede detener el barrido en la primera ocurrencia.&lt;br /&gt;&lt;br /&gt;Recuerden siempre primero medir sus resultados actuales y luego comparar contra el optimizado. Créanme que es un mundo de diferencia.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-113702557040177919?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/113702557040177919/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=113702557040177919&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113702557040177919'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113702557040177919'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/2006/01/tips-para-optimizacin-de-queries-en.html' title='Tips para optimización de queries en SQL Server (1)'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20788959.post-113702536651895221</id><published>2006-01-11T19:21:00.000-05:00</published><updated>2006-01-11T19:22:46.526-05:00</updated><title type='text'>Sobre el origen de Best Application Performance</title><content type='html'>Best Application Performance es un blog –o bitácora- dedicado a la mejora del rendimiento de aplicaciones. Aquí trataremos diversos tópicos alrededor de cómo podemos hacer para mejorar en general el rendimiento –o performance- de una aplicación.&lt;br /&gt;&lt;br /&gt;Desde ya están invitados a proponer temas, así como recurrir a los comentarios de algún tópico específico para poder revisarlos en conjunto y encontrar esas soluciones a veces tan esquivas.&lt;br /&gt;&lt;br /&gt;Quien esto escribe es Guillermo Sornoza Ortega.  Ingeniero de Sistemas, 29 años al momento de escribir esto.  También hago comentarios sobre política y sociedad ecuatoriana en &lt;a href="http://www.elecuadordehoy.org"&gt;El Ecuador de Hoy&lt;/a&gt;, así como de cuando en cuando publico artículos en &lt;a href="http://www.bloggus.net"&gt;Bloggus&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Mis especialidades son el desarrollo de aplicaciones Web con ASP.NET; administración, diseño e implementación de base de datos, en particular SQL Server.  De igual manera he trabajado y tengo amplia experiencia en desarrollo de DataWarehouse; así como desarrollo para dispositivos móviles y de escritorio.  De igual manera poseo experiencia en administración de proyectos, utilizando metodologías formales de desarrollo.&lt;br /&gt;&lt;br /&gt;Espero que el blog les sea lo más útil posible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-113702536651895221?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/113702536651895221/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=113702536651895221&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113702536651895221'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/113702536651895221'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/2006/01/sobre-el-origen-de-best-application.html' title='Sobre el origen de Best Application Performance'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20788959.post-114306826977650580</id><published>1990-03-22T17:35:00.000-04:00</published><updated>2006-03-22T17:57:49.790-05:00</updated><title type='text'>Consideraciones en los WebServices: WebMethods</title><content type='html'>Sabemos que cuando a los métodos públicos de un Web Service le colocamos el atributo WebMethod, lo estamos exponiendo para llamadas remotas.  Para esto también es necesario tener en cuenta algunas cosillas:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Parámetros con tipos nativos.&lt;/span&gt;  Con esto minimizamos la serialización, dado que se hace una validación automática gracias al .NET Framework.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Buffering.&lt;/span&gt; Por defecto, la configuración de buffer (BufferResponse) está fijada en verdadero, para asegurar que la respuesta estará en el buffer completamente antes de pasar al cliente.  Esto es bueno cuando se retorna pequeños datos, pero obviamente, en grandes cantidades, es preferible deshabilitar el buffering.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;[webmethod(bufferresponse=false)]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Public function GetTextFile() as string&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Begin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;End&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Almacenar los datos en caché.&lt;/span&gt;  Siempre y cuando los datos a retornar no sean muy volátiles.  Se puede especificar el tiempo –en segundos- que la respuesta estará almacenada en caché.  Pero ojo, dado que el caché consume memoria del servidor, no sería apropiado si el WebMethod devuelve mucha información.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;[WebMethod(cacheduration=60)]&lt;br /&gt;Public function GetCompanyName() as string&lt;br /&gt;Begin&lt;br /&gt;...&lt;br /&gt;End&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20788959-114306826977650580?l=best-performance.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://best-performance.blogspot.com/feeds/114306826977650580/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20788959&amp;postID=114306826977650580&amp;isPopup=true' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/114306826977650580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20788959/posts/default/114306826977650580'/><link rel='alternate' type='text/html' href='http://best-performance.blogspot.com/1990/03/consideraciones-en-los-webservices.html' title='Consideraciones en los WebServices: WebMethods'/><author><name>Guillermo</name><uri>http://www.blogger.com/profile/16566268978702180850</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
