Buenas prácticas en procedimientos almacenados (SQL Server 2005)

Por un encargo, estoy revisando algunas buenas prácticas que recomiendan nuestros amigos de Microsoft, algunas ya las conocía pero otras me resultan bastante interesantes (dado que cometo esos errores muy a menudo XD).
Considero importante recalcar que estas recomendaciones están "optimizadas" para SQL 2005 pero muchas de ellas (por un tema lógico) pueden ser usadas también para las versiones posteriores de éste motor de BD.
Considero importante también que no haré este manual muy "newbie", por lo que si se tiene alguna pregunta (y sé la respuesta) lo conversaremos por los mensajes al post.
Empezamos...

Nunca... pero nunca...

  1. Uses tablas globales (##NombreTabla)
  2. Uses "Select * From". Es mejor definir los nombres de las columnas. Y si tu query tiene varias tablas (joins) define correctamente los alias y asocia los alias a las columnas (incluso si no existe la posibilidad de ambiguedad) dado que el motor demoraría más en identificar qué columna pertenece a qué tabla.
  3. Uses cursores... NUNCA! (sobretodo en sistemas OLTP)


NOLOCK
No hay mucho que decir de éste método (cualquier persona con más de 1 mes en SQL lo debería conocer), pero se me hace importante mencionar que se tiene planificado deprecar (véase como "ya no usar") éste método en futuras versiones de SQL por lo que se debería dejar de usar en nuevos desarrollos y considerar el ir migrando los desarrollos ya realizados.
Recomendaría usar WITH (REPEATABLEREAD) o alguna de sus derivaciones [http://msdn.microsoft.com/es-es/library/ms173763.aspx]

SQL Dinámico
Citando:
· EL SQL dinámico debe manejarse tomando en cuenta el nivel de riesgo que las aplicaciones pueden correr al estar expuestos a un tipo de ataque de Inyección SQL
· En términos prácticos el uso del Exec presenta dos inconvenientes: el primero es a nivel de seguridad puesto que su estructura hace que  las entradas realizadas por el usuario sean potencialmente más riesgosas a ataques de inyección SQL, y la segunda está relacionada con el rendimiento, ya que si deseáramos incluir dentro de un procedimiento almacenado código dinámico este obligaría a que se realizara una compilación cada vez que se ejecuta, perdiendo de esta manera una de las principales ventajas de los procedimientos almacenados como es la reutilización de los planes de ejecución.
· Utilizando el sp_executesql no se tiene que contar con la autoparametrización de SQL Server, ya que se suministra los parámetros. Así, es más probable que el SQL Server utilice un plan de consultas que ya existe en la caché
En resumen... Es mejor usar sp_executesql (y hacer un query al que le puedas enviar parámetros) a usar sólo Exec, dado que el primero genera un plan de consultas, se queda en caché y se procesa más rápido (la mísma lógica que usar procedimientos).

Procedimientos Almacenados
  1. Los procedimientos almacenados de las aplicaciones no deben hacer llamadas a procedimientos almacenados del sistema. Por ejemplo: sp_updatestats
  2. El número máximo de filas devueltas al cliente debería ser de 200 registros. Si necesitas devolver más valores, usa paginación.
  3. En el plan de ejecución no deberá existir ningún table scan, salvo que sea de una tabla maestra con menos de 20 registros y es deseable que todas las consultas a las tablas existentes utilicen un table clustered index seek. Más info en http://msdn.microsoft.com/es-es/library/ms175913.aspx
  4. Si utiliza el comando IF para ejecutar sentencias T-SQL distintas dentro de cada bloque del procedimiento almacenado, es mejor separar cada bloque en un procedimiento almacenado distinto, porque originara que el plan de ejecución cambie en función de la ejecución del valor de la expresión en el IF, perdiéndose la ventaja de la pre compilación y del plan de ejecución en memoria. Es decir, has un procedimiento "cascarón" que llame a otros procedimientos de acuerdo a la condición que se cumpla.
  5. No escribir sentencias con los valores en duro, en su lugar use variables que sean del mismo tipo que la columna de la tabla. Esto permite eliminar las conversiones implícitas y mejora le legibilidad del código
  6. No dejar código comentado dentro de los procedimientos almacenados.
  7. Se recomienda que un procedimiento no sobrepase las 400 líneas de código (sin contemplar los comentarios). En caso que tengas un procedimiento demasiado grande es más adecuado particionarlo. Tener en cuenta que las tablas temporales y transacciones se mantienen en procedimientos dependientes.
  8. Configurar set nocount on al inicio del procedimiento almacenado para anular el mensaje de filas afectadas dado que generan procesamiento innecesario.
  9. No usar GOTO... en remplazo usar Try Catch

Datos Temporales
Cuando los datos que se desea almacenar no son muchos, se debe preferir utilizar variables de tipo tabla, pues estas mantienen los datos en memoria evitando tener que ir a disco.
Si se necesita consultar repetitivamente los mismos datos es necesario crear índices temporales sobre los campos de la tabla temporal, esto no es posible con las otras alternativas de almacenamiento temporal por lo que se recomienda utilizar tablas temporales para este caso.
Evalúe reemplazar las tablas derivadas por JOINs. 

Selects
  1. Evite poner la sentencia select dentro de los campos de selección.
  2. No use la cláusula into nombre de tabla (“SELECT… INTO”). Esto bloqueará mientras se ejecuta la consulta las tablas del sistema. En su lugar cree primero las tablas y luego re-escribe la sentencia como INSERT INTO tabla_name SELECT.
  3. Si usa el operador UNION y existe la seguridad de que ambos select NO tienen registros duplicados, entonces es mejor usar UNION ALL, para evitar que implícitamente se haga uso del operador DISTINCT el cual puede requerir que se almacenen todos los datos de salida en una tabla temporal para que luego se reordenen y se filtren los datos duplicados, lo cual aumenta considerablemente el costo de la consulta.
  4. Es recomendable usar joins a un subquery

Where
  1. Las columnas filtro TIENEN QUE SER del mismo tipo de la columna que existe en la tabla (para evitar conversiones al momento de ejecución)
  2. No usar funciones sobre columnas que están en el Where dado que SQL no tiene índices basados en funciones por lo que tendría que recorrer toda la tabla.
  3. No usar concatenaciones de cadenas
  4. Si se usa LIKE en la cláusula WHERE, se debe evitar el uso del operador "%" al principio de la cadena a buscar dado que originaria que se tienen que leer todos los datos de la tabla para poder responder dicha consulta, adicionalmente es recomendable que existan (como mínimo) 3 caracteres antes del operador "%".
  5. Dentro de lo que sea posible, usar BETWEEN en lugar de IN.
  6. Si una consulta tiene uno o más operadores OR, considera reescribir la consulta en varias consultas que se unen usando el operador UNION ALL.
Iré revisando algunas otras recomendaciones e iré organizando mejor este tema.

2 comentarios:

Unknown dijo...

Muy buenas recomendaciones

Janine dijo...

Interesantes recomendaciones.

Publicar un comentario