jueves, 7 de febrero de 2013

Tips buenas prácticas SQL


OBJETIVO

Cada día se necesita procesar mayor cantidad de datos y obtener de manera más rápida y precisa la información. Muchos de los problemas de rendimiento se deben entre otras cosas al hardware, al software, al motor de base de datos y por sobre todo al diseño, índices y mala formulación de consultas SQL. En este documento nos centraremos en las normas de programación en donde siguiendo algunas recomendaciones veremos que se puede mejorar el tiempo de respuesta de nuestro motor de BD significativamente.



NORMAS DE PROGRAMACION T-SQL

En la instrucción de SELECT debe solo recuperar lo necesario, nunca datos de más. Por otro lado nunca utilizar la sentencia SELECT *, porque el gestor lee primero la estructura antes de ejecutar la sentencia.

Seleccionar solo aquellos campos que se necesiten, cada campo extra genera tiempo extra.

Escribir las consultas con estructura ANSI y NO con estructuras T-SQL

Ejemplo estructura ANSI
SELECT <Campo1>, <Campo2> FROM <Nombre Tabla1> INNER JOIN <Nombre Tabla2> ON
<PK_TBL1> = <FK_Tbl2>
WHERE <Condición>

Ejemplo estructura T-SQL
SELECT <Campo1>, <Campo2> FROM <Nombre Tabla1>, <Nombre Tabla2> WHERE
<PK_Tbl1> = <FK_Tbl2> and <Condición>

Dado lo anterior, para realizar las relaciones entre las tablas, se deben utilizar las instrucciones:

  • INNER JOIN
  • LEFT JOIN
  • RIGHT JOIN
  • CROSS JOIN

Si se utiliza varias tablas en la consulta, hay que especificar siempre a que tabla pertenece cada campo. Esto ahorra al gestor el tiempo de localizar a que tabla pertenece el campo.

Ejemplo

En lugar de:

SELECT Nombre, Factura
FROM Clientes inner join Facturacion on IdCliente =IdClienteFacturado

Usar:

SELECT Clientes.Nombre, Facturacion.Factura
FROM Clientes inner join Facturacion on Clientes.IdCliente =
Facturacion.IdClienteFacturado

No utilizar frecuentemente la cláusula LIKE, en el caso de utilizarla es recomendable realizar Like ‘m%’ que %m, por ejemplo, ya que en el primer caso, el plan de ejecución de SQL determina y recorre el registro por el índice que contiene la tabla, siempre y cuando el índice exista.

Utilizar en vez de la cláusula IN la cláusula BETWEEN, cuando sea posible en la instrucción
WHERE.

Utilizar lo menos posible las cláusulas ANY, SOME, IN (SELECT), NOT, IS NULL, != , <>, !>, !<,
NOT EXISTS, NOT IN, NOT LIKE, LIKE ‘%ab.

No utilizar tablas temporales públicas. En el caso que se utilicen tablas temporales locales en algún procedimiento, estas siempre se deben eliminar al terminar de utilizarse en el procedimiento en cuestión, de igual modo antes de crear alguna tabla temporal local, siempre se debe ver la opción de utilizar variables tipo tabla como prioridad.

Utilizar la instrucción TOP si necesita una cantidad limitada de filas.

La instrucción TOP nos devuelve la cantidad de filas que nosotros le especifiquemos.

Ejemplo

Esto nos devolverá las primeras 10 filas de la tabla “<Nombre Tabla1>”:
SELECT TOP 10 <Campo1> FROM <Nombre Tabla1>

Usar la cláusula EXISTS en lugar de la cláusula IN. La cláusula EXISTS es ligeramente más rápida.

Utilizar la cláusula EXISTS en vez de la sentencia SELECT Count(*) FROM…, esta práctica generalmente se realiza con la instrucción IF.

En el caso de que se esté utilizando la cláusula EXIST de este modo:

SELECT <Campo1> FROM <Nombre Tabla1>
WHERE EXISTS (SELECT * FROM <Nombre Tabla2>)

Lo óptimo es hacer lo siguiente:

SELECT <Campo1> FROM <Nombre Tabla1>
WHERE EXISTS (SELECT TOP 1 <Campo1> FROM <Nombre Tabla2>)

La misión de la función EXIST en este caso es corroborar si existe o no X valor, por esta misma razón lo optimo es hacerlo solo llamando un campo y haciendo un TOP 1 sobre la consulta anidada ya que de esta manera no se pierde el sentido de la consulta porque el efecto será el mismo. De esta manera hacemos que la consulta sea mucho más liviana.

Evitar usar la instrucción UNION, a menos que este eliminado filas duplicadas.

No utilizar la instrucción ROUND, LOWER, UPPER, SUBSTRING en el WHERE.

Reemplazar Count(*) por Count(1) o Count(<Nombre del campo>)

No Utilizar WITH NOLOCK o WITH ROWLOCK porque esta sentencia puede leer la tabla aun cuando tenga transacciones pendientes (update, delete e insert), por ende podría mostrarse y leerse información que puede no sea real.

Definir variables con el tipo de datos adecuados al dato a almacenar. Porque si no es así, podría haber errores al almacenar algún dato, o al intentar hacer alguna operación con el dato en cuestión.

Se debe colocar el esquema correspondiente a la tabla que se defina.

Ejemplo
BASE.. TABLA

Reemplazar por
BASE.dbo.TABLA

Como buena práctica también se anima a incluir en el código la instrucción SET NOCOUNT ON, ya que esta instrucción evita que se devuelva el mensaje que muestra el recuento del número de filas afectadas por una instrucción o un procedimiento almacenado como parte del conjunto de resultados. Si se establece SET NOCOUNT en ON, no se devuelve el recuento. Cuando SET NOCOUNT es OFF, sí se devuelve ese número.

No utilizar ejecuciones de código DINAMICO, ya que con esta forma no se sabe si la sintaxis es correcta hasta cuando se ejecutan los procesos, El Servidor tiene que compilar y luego ejecutar.

Utilizar CASE de tal modo de suplir la necesidad de usar código dinámico. La sentencia CASE se utiliza para brindar un tipo de lógica "si-entonces-otro" para SQL.

Indentar u ordenar el código fuente del programa para mejorar la legibilidad por parte de los programadores.


CURSORES

Evitar utilizar Cursores sin antes ver la posibilidad de que la misma operación se realice por medio de sentencia SQL.

Utilizar el tipo de datos Table en vez de cursores, pero solo cuando estas tengan pocos datos.

Ejemplo

Declare @VarTable TABLE (<Nombre Campo1> <Tipo de dato>, <Nombre Campo…n> <Tipo de dato>)

Solo se aceptaran declaraciones cursores para aquellos que apunten a tablas temporales, dentro de todo lo malo. Y además que contengan la siguiente estructura.

Solo lectura (Read_Only)

Solo de una dirección de lectura. (Fast_Forward )


Ejemplo

DECLARE objects_cursor CURSOR LOCAL FAST_FORWARD READ_ONLY FOR

En el caso de que en un IF venga lo siguiente:

IF (SELECT COUNT (1) FROM <Nombre Tabla1>) > 0 …

 Lo óptimo es hacer lo siguiente:

IF EXIST (SELECT <Campo1> FROM <Nombre Tabla1>)…

Este método es mucho más rápido ya que el primero cuenta todas las filas para poder traer un resultado, mientras que la segunda opción busca de inmediato si existe o no alguna fila para así seguir efectuando la consulta formulada.

9 comentarios:

  1. Me sirvio mucho tu articulo... saludos!

    ResponderEliminar
  2. Buen aporte, ahora el tema es poner en practica con BDD pequeñas y grandes, para poder visualizar la diferencia.

    ResponderEliminar
  3. Buenas! Buen artículo. Algo que estaría bueno aclarar con WITH (NOLOCK) es que en buena cantidad de ocasiones es recomendable hasta obligatorio usarlo. Por ejemplo en un proceso batch o cuando no necesitamos datos actualizados, es recomendable usarlo ya que no necesitamos las últimas transacciones. Diferente cuando necesitamos que la consulta nos traiga hasta el último dato disponible. Saludos

    ResponderEliminar
  4. Buenas Tardes, ¿si no se debe usar NOT IN porque sentencia se puede reemplazar?

    ResponderEliminar