miércoles, 19 de agosto de 2009

Mejorar rendimientos Queries SQL

Hoy les traigo unas técnicas de como mejorar el rendimiento de sus consultas a SQL Server y así obtener mejores resultados de los desarrollos de nuestras aplicaciones. A continuación algunos trucos de como mejorar el desempeño de los queries en SQL:

Uso forzado de índices: muchas veces escuchamos que si la consulta que realizamos no tenemos que especificar el nombre del índice que estamos usando ya que el motor de base de datos lo asume por defecto. Cuando tenemos tablas grandes y que manejan muchos datos, esto no es del todo correcto. podemos mejorar hasta un 70% en el rendimiento de una consulta si forzamos el query y especificamos el nombre del índice a utilizar. Ejemplo:

select * from countries (index=idx_countryname,nolock)

Nota: la sentencia nolock hace que la consulta no provoque bloqueos en la base de datos, especialmente cuando traemos datos mediante la sentencia TOP que realiza bloqueos en las tablas. Lo malo de usar NOLOCK es que podemos traer registros que estén en medio de una transacción.

Uso de Exists: cuando queramos hacer una sub-consulta en una base de datos utilizando la sentencia NOT IN, analicemos si podemos cambiar nuestro queries con el uso de la sentencia Exists que es mucho más eficiente que la anterior. O en todo caso utilizar IN en vez de NOT IN, ya que esto hace un scan completo en la tabla descartando opciones a omitir. Ejemplo:

select * from countries (index=idx_countryname,nolock) where countryname not in (select name from blacklist)
Optimizado con exists:
select * from countries a (index=idx_countryname,nolock) where exists (select * from blacklist b where a.countryname = b. name)

Uso de Distinct: utilizar distinct para excluir datos duplicados es muy usado por los programadores para evitar errores de diseño de base de datos y así esconder algunos duplicidad de información, pero esto es un grave error. Es una de las sentencias que más necesita hacer I/O en el disco y forza bastante el procesados. Por tal motivo, si es necesario evitemos utilizarla.

Uso de Top: cuando se quiere traer un grupo de registros es mejor utilizar la sentencia Top y no Rowcount, ya que esta última presenta algunos inconvenientes con listas no oldenadas, en caso contrario, si la lista es ordenada, es más eficiente que la sentencia Top.

select top 10 countryname from countries with(nolock)
es más eficiente que:
set rowcount 10
select countryname from countries with(nolock)

en caso de que los registros tengan un índice para el campo countryname que ordene los registros utilizar rowcount puede ser más eficiente que el top.

Uso de *: cuando estamos realizando consultas que van a devolver muchos campos es mejor definir todos los campos que queremos devolver en nuestro queries, ya que el uso de * o All impide el uso de índices de forma eficiente.

Usos de otras tablas ara guardar datos sumarizados: si queremos presentar datos sumarizados o agrupados de una tabla que maneja miles o quizás millones de registros, es mejor utilizar un trigger que guarde la información en una tabla auxiliar o mediante un proceso que se ejecute en background y recalcule nuestros registros.

Verificar si existe un registro: muchos programadores utilizan el count(*) para ver si un registro existe en la base de datos, pero una forma más eficiente de hacerlo es con el Exists, ya que cuando este encuentra un registro detiene la búsqueda del mismo.

4 comentarios:

Alnair dijo...

Muy interesante e informativo.

Gracias.

jose eduardo Martinez dijo...

interesante pero yo tengo problemas con consultas de 2 millones de registros con c# me manda un error de memoria nose por que espero me auxiles

Michael Núñez dijo...

Hola, 2 millones de registro es mucho, no se para qué quieres consultar esa cantidad y traerlo a memoria, pero el problema no es de base de datos, el error de memoria es provocado por la aplicación cliente cuando intenta traer todos esos registros a memoria.

JMmak dijo...

select * from countries (index=idx_countryname,nolock) where countryname not in (select name from blacklist)

Optimizado con exists:

select * from countries a (index=idx_countryname,nolock) where exists (select * from blacklist b where a.countryname = b. name)

Sólo puntualizar que para obtener los mismos resultados habría que utilizar NOT EXISTS en este caso, en lugar de EXISTS.