You are on page 1of 30

Buenas prcticas en procedimientos almacenados (SQL Server 2005)

Por un encargo, estoy revisando algunas buenas prcticas que recomiendan


nuestros amigos de Microsoft, algunas ya las conoca pero otras me resultan
bastante interesantes (dado que cometo esos errores muy a menudo XD).
Considero importante recalcar que estas recomendaciones estn "optimizadas"
para SQL 2005 pero muchas de ellas (por un tema lgico) pueden ser usadas
tambin para las versiones posteriores de ste motor de BD.
Considero importante tambin 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...

Uses tablas globales (##NombreTabla)


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
demorara ms en identificar qu columna pertenece a qu tabla.
Uses cursores... NUNCA! (sobretodo en sistemas OLTP)

NOLOCK
No hay mucho que decir de ste mtodo (cualquier persona con ms de 1 mes en
SQL lo debera conocer), pero se me hace importante mencionar que se tiene
planificado deprecar (vase como "ya no usar") ste mtodo en futuras versiones
de SQL por lo que se debera dejar de usar en nuevos desarrollos y considerar el
ir migrando los desarrollos ya realizados.
Recomendara usar WITH (REPEATABLEREAD) o alguna de sus derivaciones
[http://msdn.microsoft.com/es-es/library/ms173763.aspx]
SQL Dinmico
Citando:

EL SQL dinmico debe manejarse tomando en cuenta el nivel de riesgo que


las aplicaciones pueden correr al estar expuestos a un tipo de ataque de Inyeccin
SQL
En trminos prcticos 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 ms riesgosas a ataques de inyeccin SQL, y
la segunda est relacionada con el rendimiento, ya que si deseramos incluir
dentro de un procedimiento almacenado cdigo dinmico este obligara a que se
realizara una compilacin cada vez que se ejecuta, perdiendo de esta manera una
de las principales ventajas de los procedimientos almacenados como es la
reutilizacin de los planes de ejecucin.
Utilizando el sp_executesql no se tiene que contar con la autoparametrizacin
de SQL Server, ya que se suministra los parmetros. As, es ms 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 parmetros) a usar slo Exec, dado que el primero genera un plan de
consultas, se queda en cach y se procesa ms rpido (la msma lgica que usar
procedimientos).

Procedimientos Almacenados

Los procedimientos almacenados de las aplicaciones no deben hacer llamadas


a procedimientos almacenados del sistema. Por ejemplo: sp_updatestats
El nmero mximo de filas devueltas al cliente debera ser de 200 registros. Si
necesitas devolver ms valores, usa paginacin.
En el plan de ejecucin no deber existir ningn 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. Ms info en
http://msdn.microsoft.com/es-es/library/ms175913.aspx
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 ejecucin
cambie en funcin de la ejecucin del valor de la expresin en el IF, perdindose la
ventaja de la pre compilacin y del plan de ejecucin en memoria. Es decir, has un
procedimiento "cascarn" que llame a otros procedimientos de acuerdo a la
condicin que se cumpla.
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 implcitas y mejora le legibilidad del cdigo
No dejar cdigo comentado dentro de los procedimientos almacenados.
Se recomienda que un procedimiento no sobrepase las 400 lneas de cdigo
(sin contemplar los comentarios). En caso que tengas un procedimiento
demasiado grande es ms adecuado particionarlo. Tener en cuenta que las tablas
temporales y transacciones se mantienen en procedimientos dependientes.
Configurar set nocount on al inicio del procedimiento almacenado para anular el
mensaje de filas afectadas dado que generan procesamiento innecesario.
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.
Evale reemplazar las tablas derivadas por JOINs.

Selects

Evite poner la sentencia select dentro de los campos de seleccin.


No use la clusula 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.
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
implcitamente 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.
Es recomendable usar joins a un subquery

Where

Las columnas filtro TIENEN QUE SER del mismo tipo de la columna que existe
en la tabla (para evitar conversiones al momento de ejecucin)
No usar funciones sobre columnas que estn en el Where dado que SQL no
tiene ndices basados en funciones por lo que tendra que recorrer toda la tabla.
No usar concatenaciones de cadenas
Si se usa LIKE en la clusula 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 mnimo) 3 caracteres antes del operador "%".
Dentro de lo que sea posible, usar BETWEEN en lugar de IN.
Si una consulta tiene uno o ms 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.

Consultas SQL ms costosas

Algunas veces, sobre todo cuando tenemos entre manos un proceso de


optimizacin, es necesario saber exactamente cules son las sentencias que
tienen un costo o tiempo de ejecucin mayor para poder enfocar nuestras fuerzas
en se punto.
Para suerte nuestra, en SQL se guarda un histrico de todas las veces que se
ejecuta una sentencia, por lo que podemos consultar no slo cuantas veces se ha
ejecutado (en el caso de los procedimientos, la cantidad de veces se cuenta desde
su ltima compilacin) si no que podemos ver el tiempo y costo (de CPU)
promedio de todas las ejecuciones, el tiempo y costo de la ltima ejecucin entre
otros datos ms.

SELECT TOP 10
DB_NAME(qt.dbid) 'Base de Datos',
OBJECT_NAME(qt.objectid,qt.dbid)AS 'Nombre Objeto',
SUBSTRING(qt.text, (qs.statement_start_offset/2)+1,
((CASE qs.statement_end_offset
WHEN -1 THEN DATALENGTH(qt.text)
ELSE qs.statement_end_offset
END - qs.statement_start_offset)/2)+1) AS 'Texto',
qs.execution_count AS 'Veces ejecutado',
qs.total_logical_reads AS 'Total lecturas lgicas',
qs.last_logical_reads AS 'Lecturas lgicas del ltimo proceso',
qs.total_logical_writes AS 'Total escrituras lgicas',
qs.last_logical_writes AS 'Escrituras lgicas del ltimo proces',
qs.total_worker_time AS 'Total tiempo CPU',
qs.last_worker_time AS 'Tiempo CPU del ltimo proceso',
qs.min_worker_time AS 'Minimo tiempo CPU',
qs.max_worker_time AS 'Maximo tiempo CPU',
qs.total_elapsed_time/1000000 AS 'Total tiempo (en seg)',
qs.last_elapsed_time/1000000 AS 'Tiempo del ltimo proceso (en seg)',
qs.min_elapsed_time/1000000 AS 'Tiempo mnimo (en seg)',
qs.max_elapsed_time/1000000 AS 'Tiempo mximo (en seg)',
qs.last_execution_time AS 'Ultima vez que se ejecut',
qp.query_plan AS 'Plan de ejecucin'
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
--WHERE DB_NAME(qt.dbid) = 'NOMBRE_DE_BD'
ORDER BY qs.total_elapsed_time DESC

Siempre pueden listar ms items (quitando el TOP 10), ordenar el resultado por
otro factor (cambiando el ORDER BY) o restringiendo la bsqueda a una base de
datos especfica (descomentando la penltima lnea y cambiando
NOMBRE_DE_BD por el nombre de su Base de Datos)
Probar procedures en SQL

sta entrada ser muy corta.

Hace algn tiempo me dijeron "tienes que optimizar este procedure", as que lo
analic, hice algunas mejoras, y en la primera corrida del procedure, demor
aproximadamente 3 veces ms de lo que demoraba el procedure original, pens
que haba un error dado a que haba aplicado (segn yo) las "buenas prcticas",
as que lo ejecut denuvo y demor la mitad del procedure original.

Que pas? Sencillo, el cach y el buffer.

Tengan en cuenta que la ventaja de un procedure, es que compila la consulta y la


guarda compilada, (es por eso que es una muy buena prctica el usuarlos) y lo
que pas fue que en la primera ejecucin de mi modificacin, es procedure tuvo
que eliminarse y volverse a compilar.

Buscando un poco con internet, di con un mtodo que me ayudara un poco con
esto de los tiempos (dejando de lado el usar el analizador de querys obviamente) y
es el hecho de simplemente limpiar el cach y el buffer antes de ejecutar el
procedure y esto se hace con los siguientes mtodos

DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE

Tips buenas prcticas SQL

OBJETIVO

Cada da se necesita procesar mayor cantidad de datos y obtener de manera ms


rpida y precisa la informacin. 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 diseo, ndices y mala formulacin de consultas SQL. En este documento
nos centraremos en las normas de programacin 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 instruccin de SELECT debe solo recuperar lo necesario, nunca datos de


ms. 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 <Condicin>

Ejemplo estructura T-SQL


SELECT <Campo1>, <Campo2> FROM <Nombre Tabla1>, <Nombre Tabla2>
WHERE
<PK_Tbl1> = <FK_Tbl2> and <Condicin>

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 clusula LIKE, en el caso de utilizarla es


recomendable realizar Like m% que %m, por ejemplo, ya que en el primer caso,
el plan de ejecucin 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 clusula IN la clusula BETWEEN, cuando sea posible en la


instruccin
WHERE.

Utilizar lo menos posible las clusulas ANY, SOME, IN (SELECT), NOT, IS NULL, !
= , <>, !>, !<,
NOT EXISTS, NOT IN, NOT LIKE, LIKE %ab.
No utilizar tablas temporales pblicas. En el caso que se utilicen tablas temporales
locales en algn procedimiento, estas siempre se deben eliminar al terminar de
utilizarse en el procedimiento en cuestin, de igual modo antes de crear alguna
tabla temporal local, siempre se debe ver la opcin de utilizar variables tipo tabla
como prioridad.

Utilizar la instruccin TOP si necesita una cantidad limitada de filas.

La instruccin 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 clusula EXISTS en lugar de la clusula IN. La clusula EXISTS es


ligeramente ms rpida.

Utilizar la clusula EXISTS en vez de la sentencia SELECT Count(*) FROM,


esta prctica generalmente se realiza con la instruccin IF.

En el caso de que se est utilizando la clusula 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 misin de la funcin EXIST en este caso es corroborar si existe o no X valor,


por esta misma razn 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 ms liviana.

Evitar usar la instruccin UNION, a menos que este eliminado filas duplicadas.

No utilizar la instruccin 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 podra mostrarse y leerse informacin que puede no sea real.

Definir variables con el tipo de datos adecuados al dato a almacenar. Porque si no


es as, podra haber errores al almacenar algn dato, o al intentar hacer alguna
operacin con el dato en cuestin.

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

Ejemplo
BASE.. TABLA

Reemplazar por
BASE.dbo.TABLA

Como buena prctica tambin se anima a incluir en el cdigo la instruccin SET


NOCOUNT ON, ya que esta instruccin evita que se devuelva el mensaje que
muestra el recuento del nmero de filas afectadas por una instruccin 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 nmero.

No utilizar ejecuciones de cdigo 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 cdigo dinmico. La


sentencia CASE se utiliza para brindar un tipo de lgica "si-entonces-otro" para
SQL.

Indentar u ordenar el cdigo 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 operacin 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


Campon> <Tipo de dato>)

Solo se aceptaran declaraciones cursores para aquellos que apunten a tablas


temporales, dentro de todo lo malo. Y adems que contengan la siguiente
estructura.
Solo lectura (Read_Only)

Solo de una direccin 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 mtodo es mucho ms rpido ya que el primero cuenta todas las filas para
poder traer un resultado, mientras que la segunda opcin busca de inmediato si
existe o no alguna fila para as seguir efectuando la consulta formulada.

Buenas prcticas en construccin de Consultas SQL

Esta entrada es una recopilacin de muchos aos, en los cuales muchas personas
me brindaron tips respecto a cmo implementar consultas en RDBMS con alto
rendimiento.
Hace poco algunos compaeros de trabajo me solicitaron que compartiera este
conjunto de prcticas con el fin de que fueran evolucionando; Muchos pueden
aportar.

Si bien no estn todos los posibles tips en afinamiento, si hay un conjunto bsico
de prcticas a seguir, que espero les sea til.

Posdata: si tienen otra prctica a incluir, por favor dejen el comentario para
incluirla.

El conocimiento no sirve de nada si no se comparte

A continuacin la lista de prcticas:

Problema / Contexto
Solucin
Justificacin
Optimizar el producto cartesiano de las tablas a utilizar en una consulta.
Prefiere los JOINS en todas sus variedades (Left, right, inner,etc.) en el FROM
antes que en el WHERE.
1). Siempre lo primero que hace un motor de BD es ejecutar la sentencia FROM
realizando un producto cartesiano.
2). Luego, ejecuta la clausula where.
3). Calcula las filas del SELECT.
4). Si existe un select distinc, ordena y luego elimina las filas duplicadas.
5). Si existe el order by, ordena las filas.

Si se evitan los joins en el where mejorars el desempeo pues se aprovechar el


producto cartesiano.
Siempre las tablas que tienen menos registros van al lado izquierdo en el
FROM y en el WHERE. Es fundamental el orden.
Ejemplo: FROM TABLA1, TABLA2, TABLA3.

La TABLA1 debe tener menos registros que la TABLA2 y esta menos que la
TABLA3. Lo anterior porque recuerda que en el FROM siempre se realiza un
producto cartesiano (revisar algebra y clculo relacional)
Se aplica la teora de conjuntos, en dnde la tabla con menos registros intercepta
a la que tiene ms registros, y por tal motivo si se enlaza una tercera se tendr un
conjunto a interceptar ms pequeo, agilizando la consulta pues se manejan
menos datos.
Nunca uses sentencias LIKE al iniciar un where.
Es importante asegurarse que ya se han filtrado los datos para que el conjunto de
datos objetivo sea ms pequeo y por lo tanto manejable en el caso en que se
requiera el uso de LIKE. Es importante recordar que la sentencia LIKE implica que
no se usen ndices.
Las sentencias LIKE no utlizan ndices. Evita el LIKE "%AS%", revisa si lo que
requieres es un LIKE "%AS" o un LIKE "AS%"
Nunca use DISTINCT
Use EXISTS. Por ejemplo: Escriba la consulta como

SELECT d.dept_id, d.dept


FROM dept d
WHERE EXISTS ( SELECT 'X' FROM employee e WHERE e.dept = d.dept);

En lugar de:

SELECT DISTINCT d.dept_id, d.dept


FROM dept d,employee e
WHERE e.dept = e.dept;
Si usas un DISTINCT implicar que primero el motor ejecute la consulta y luego la
ordene para luego eliminar los registros duplicados. prefiera un: SELECT X1, X2
FROM (SELECT X1, X2, FROM Y WHERE.....) GROUP BY X1, X2
Evite el ORDER BY

Es un paso adicional a veces innecesario que retarda una consulta.


Use un subconjunto ordenado de la definicin de un ndice en el WHERE
para garantizar que lo utilice.
Use un subconjunto ordenado de la definicin de un ndice para asegurarse que
sigue un plan de ejecucin deseado.
Si le ayudas al motor mucho mejor. Si tienes un ndice de una tabla X con los
siguientes campos: x1,x2 y x3; en el WHERE debes siempre colocar de arriba a
abajo: WHERE x1=? (AND/OR..) x2=? (AND/OR..) x3=? o por ejemplo WHERE
x1=? (AND/OR..) x2=?, pues siempre ser ms rpido que un WHERE x3=?
(AND/OR..) x1=?, etc...le facilitars al motor el establecimiento del plan de datos y
el uso de las memorias compartidas de ste (consultas frecuentes)
No use SELECT *
Coloque las columnas que requiere.
El motor primero lee la estructura de la tabla y luego realiza la consulta. Hace una
bsqueda de la meta-data de la tabla y luego si coloca los campos.
Siempre utiliza consultas precompiladas
Usa consultas precompiladas en el lenguaje de programacin en el que las
realices.
No es lo mismo SELECT x.x1, x.x2 FROM x WHERE x.x1=1 que SELECT x.x1,
x.x2 FROM x WHERE x.x1=2. Mientras que para el motor si es lo mismo SELECT
x.x1, x.x2 FROM x WHERE x.x1=? para todos los casos y por tal motivo seguir
un plan de ejecucin ya definido.
Utiliza en la mayora de los casos consultas en batch
Por ejemplo usando JDBC
Envan un conjunto de consultas que se ejecutan en el motor de base de datos, en
vez de hacer llamados sucesivos ocupando recurso computacional como:
memoria, red, cpu.
Si vas a llamar varias veces a una BD para efectar consultas SQL
secuencialmente es preferible que crees un procedimiento almacenado.

Si vas a llamar varias veces a una BD para efectar consultas SQL


secuencialmente es preferible que crees un procedimiento almacenado que
contenga todas las consultas y que retorne una estructura con todos los valores
que requieras (ahorraras red, memoria...etc).
Si es muy grande el conjunto de datos requerido descarta esta idea a menos que
implementes un paginador. Cuando tengas pocas consultas no uses un
procedimiento almacenado pues ser ms costoso.
Es preferible no usar ndices para tablas que tienen pocos registros.

Es ms costoso trazar el plan de ejecucin para la BD que si se efectuar un


FULL-DATA.
Defina alias a las tablas en el SELECT con el fin de facilitarle al motor la tarea
de averiguar campo por campo a que tabla pertenece.

Se evitan las consultas intronspectivas. Se evitan accesos a la meta-data de la


BD.
Evita el casting CAST y el uso de funciones dentro de las consultas.
No utilice funciones propietarias del RDBMS
Reducen el desempeo de una consulta. Adems, es difcil afinar las consultas de
las funciones.
Siempre implementa la lgica de paginacin en BD y no en algn contenedor
Web o de lgica.

Evita la utlizacin de patrones como el ValueListHandler.


Evita el uso de intronspeccin porque sto genera una carga innecesaria.
Conclusin, no uses ORMs porque estn de moda. salos para tabla de
parmetros o de configuracin, pero para consultas que requieran eficiencia
jams. Siempre los POJOS (ValueObject, DTO, o transfer objects acompaados
de un DAO sern ms rpidos)
No uses HQL de hibernate para consultas complejas o de grandes volmenes; usa
namedQueries.
Bloqueos en Pools de conexin
Cierre siempre las conexiones y cursores (resultSets) en Java.
Existe un nmero limitado de sesiones disponibles en una base de datos.
Utilice vistas materializada para consultas recurrentes

La ventaja de una vista es que tienes un select ya hecho y te permite realizar


consultas de forma rpida y sencilla. De esta forma, por ejemplo, si en una
consulta que haces muy habitualmente hay una funcin que tarda mucho en
ejecutarse, puedes tener la vista materializada con los resultados ya pre-
calculados y acceder a los datos rpidamente.

Las vistas materializadas se actualizan o se recrean solas cada cierto tiempo, o


sea, que tambin se pueden ver como una tabla que va actualizando
automticamente sus datos cada cierto tiempo con los resultados de una select.
Evite el uso de UNION
Utilice UNION ALL
La sentencia UNION elimina los duplicados de la unin de dos consultas mientras
que el UNION ALL concatena todos los resultados y no elimina duplicados. El
desempeo mejorar usando UNION ALL

Obtencin de descripciones desde tipologas con el fin de mostrarlas en el


SELECT. Ejemplo
SELECT A.A1, A.A2, A.A4
FROM TABLEA A, TIPOLOG T
WHERE A.A1 = ? AND A.A2 = ? AND (T.T1 = A.A3 AND T.T2 = 'A' AND T.T3 = 'B')
En estos casos es mejor hacer una subconsulta en el select si la tabla base de la
consulta contiene un gran nmero de registros y las de tipologas unos pocos.
Recuerde que si la tabla de tipologas es una tabla pequea no es muy util utilizar
ndices y que una consulta resuelve el grn nmero de registros en el from y en el
where efectuando los productos cartesianos all.

Siguiendo el ejemplo anterior use:


SELECT A.A1, A.A2, (SELECT T.T4 FROM TIPOLOG T WHERE T.T1 = A.A3 AND
T.T2 = 'A' AND T.T3 = 'B') as A4
FROM TABLEA A
WHERE A.A1 = ? AND A.A2 = ?

Mejores practicas: SQL SERVER

Siempre es bueno llevar un conocimiento bsico de los estndares y mejores


prcticas en desarrollo y bases de datos.
A continuacin les proporciono con una lista de Best Practices para SQL Server
(Que aplican tambin para muchos otros DBMS):

1. No usar Select *. Siempre que se utiliza Select * todas las columnas en la tabla
o unin se incluyen en el conjunto de resultados, as que el incluir todas las
columnas aunque no sean necesarias provoca un exceso de entradas/salidas en
el servidor y un consumo innecesario del ancho de banda de la red.

2. Siempre mandar llamar procedimientos almacenados. No hay que enviar


declaraciones Select, Insert, Delete o Update a la base de datos; en vez de eso,
siempre hay que llamar procedimientos almacenados pasndole los parmetros
correspondientes.
El motivo de esta mejor prctica es el siguiente: cuando SQL Server recibe una
consulta, como una declaracin Select, lo primero que hace es compilarla, crear
un plan de ejecucin, y finalmente ejecutarlo; todos estos pasos consumen tiempo.
Cuando se invoca un procedimiento almacenado, este procedimiento almacenado
puede ser compilado si es la primera vez que es llamado, o si cambian las
estadsticas que le afecten, pero en caso contrario no es compilado y es
almacenado en el cach; el plan de ejecucin tambin es almacenado en el cach.
El llamar un procedimiento almacenado ahorra tiempo de ejecucin y
recursos, as que es una mejor prctica que no debe ser ignorada.

3. No grabar los procedimientos almacenados con un nombre con prefijo sp_.


Cuando el nombre de un procedimiento almacenado comienza con sp_, SQL
Server lo busca en el siguiente orden:
En la base de datos maestra En la base de datos determinada por los calificativos
proporcionados (nombre de la base de datos o su dueo) En cada base de datos
que tenga dbo como dueo, si el dueo no fue proporcionado.

4. Usar la clusula Join con estndar ANSI. Para unir tablas es mejor usar la
clusula Join que hacer una unin por medio de la clusula Where. A pesar de que
a partir de SQL Server 7.0 las uniones de tablas usando Where pueden ser
traducidas por el plan de ejecucin a uniones explcitas, el hecho es que el
compilador es quien hace esa conversin, lo cual le toma tiempo y recursos.

5. Evitar el uso de cursores en los procedimientos almacenados. Los cursores en


SQL Server son recursos muy caros, lo cual hace mas lento el desempeo de las
consultas. Se debe evitar en lo posible el uso de cursores.
6. Utilizar SET NOCOUNT ON. Al crear procedimientos almacenados, se puede
mejorar el desempeo de ADO eliminando los valores innecesarios de la cantidad
de renglones afectados, del conjunto de datos de salida, con solo agregar la
instruccin SET NOCOUNT ON en el procedimiento almacenado.

7. Minimizar el uso de tablas temporales. Aunque las tablas temporales


generalmente son una estructura en memoria, lo cual puede parecer que es una
solucin de acceso rpido, eso no significa que este enfoque mejore el
desempeo; de hecho, esto empeorara el desempeo. El motivo de esto es que la
estructura de una tabla temporal no la conoce de antemano el optimizador de
consultas, por lo tanto el optimizador necesita recompilar el plan de ejecucin una
vez que la conoce; esto es, despus de que la tabla temporal es creada. Muchas
veces, el tiempo que le toma recompilar el procedimiento es mayor que el tiempo
de la ejecucin misma.

8. Usar tablas derivadas siempre que sea posible. Las tablas derivadas tienen un
mejor desempeo. Considerando la siguiente consulta para encontrar el segundo
salario mas alto de la tabla de Empleados:
SELECT MIN(Salary) FROM Employees WHERE EmpID IN ( SELECT TOP 2
EmpID FROM Employees ORDER BY Salary DESC )
La misma consulta puede ser re-escrita usando una tabla derivada, como se
muestra a continuacin, y ser el doble de rpida que la consulta anterior:

SELECT MIN(Salary) FROM ( SELECT TOP 2 Salary FROM Employees ORDER


BY Salary DESC ) AS A

9. Evitar el uso de caracteres comodn al inicio de una palabra al usar el


identificador LIKE. Se debe intentar evitar el uso de caracteres comodn al inicio
de una palabra al hacer una bsqueda usando el identificador LIKE, ya que eso
ocasiona un rastreo en el ndice (index scan), lo cual se contrapone con el objetivo
de usar ndices. El primero de los siguientes cdigos genera un rastreo en el
ndice, mientras que el segundo genera una bsqueda en el ndice (index seek):

SELECT LocationID FROM Locations WHERE Specialities LIKE %pples?


SELECT LocationID FROM Locations WHERE Specialities LIKE A%s?

Tambin se deben evitar las bsquedas utilizando operadores de no igualdad (<>


y NOT) ya que
stos resultan en rastreos de ndices y tablas.

10. Evitar el uso de sugerencias (hints). Las sugerencias sobrepasan la


optimizacin de consultas y pueden prevenir que el optimizador de consultas
escoja el plan de ejecucin ms rpido. Debido a cambios en el optimizador, las
sugerencias que mejoraban el desempeo en versiones previas de SQL Server
pueden no tener efecto o incluso empeorar el desempeo en SQL Server 7.0 y
2000. Adems de esto, las sugerencias a las uniones pueden causar degradacin
del desempeo.
Las sugerencias a las uniones previenen que una consulta sea elegible para la
auto-parametrizacin y subsecuente almacenamiento en cach del plan de
ejecucin. Cuando se usa una sugerencia a la unin, implica que se quiere forzar
el orden de unin para todas las tablas en la consulta, aun y si las otras uniones
no usan explcitamente una sugerencia.
Si la consulta que se est analizando contiene cualquier sugerencia, debe
removerse y re-evaluar su desempeo.

11. Tratar de no usar tipos de datos TEXT o NTEXT para almacenar datos
textuales grandes. El tipo de datos TEXT tiene ciertos problemas inherentes a l.
Por ejemplo, no se puede grabar o actualizar datos de texto usando las
instrucciones INSERT o UPDATE. En vez de eso, es necesario usar declaraciones
especiales como READTEXT, WRITETEXT y UPDATETEXT.
Tambin existen muchos errores asociados con la replicacin de tablas que
contienen columnas de tipo TEXT. Por eso, si no se necesita almacenar ms de 8
KB de texto, es preferible usar los tipos de datos CHAR (8000) o VARCHAR
(8000).

12. De ser posible, no almacenar archivos binarios o de imagen (Binary Large


Objects o BLOBs) en la base de datos. En vez de eso, almacenar la ruta al archivo
binario o de imagen en la base de datos y usarla como apuntador al archivo actual
almacenado en otra parte del servidor. Es mejor recuperar y manipular estos
grandes archivos binarios fuera de la base de datos, y despus de todo una base
de datos no esta hecha para almacenar archivos.

13. Usar el tipo de datos CHAR para una columna solamente cuando no pueda
contener valores nulos. Si una columna CHAR puede contener valores nulos, es
tratada como una columna de ancho fijo en SQL Server 7.0+. As que un CHAR
(100) cuando sea nulo ocupara 100 bytes, resultando en un desperdicio de
espacio. Para esta situacin es mejor usar VARCHAR(100). Ciertamente las
columnas de ancho variable tienen un poco ms de overhead de procesamiento
en comparacin con lascolumnas de ancho fijo. Se debe escoger con cuidado
entre CHAR y VARCHAR dependiendo del ancho de los datos que se van a
almacenar.

OPTIMIZACION Y BUENAS PRACTICAS EN SQL SERVER


IMPLEMENTE SET NOCOUNT ON EN SUS CONSULTAS
Al ejecutar sentencias
tipo DML (http://en.wikipedia.org/wiki/Data_manipulation_language) en un procedimiento
almacenado, el servidor SQL regresa un mensaje indicando el nmero de filas afectadas
por el proceso. Aunque esta informacin puede ser til para depurar el cdigo, resulta
obsoleta si no se est depurando. Al establecer el uso de SET NOCOUNT ON, se
desactiva la funcin de retorno de esta informacin adicional. En procedimientos
almacenados formados por varias instrucciones o declaraciones de sentencias DML,
habilitar SET NOCOUNT ON puede ayudarnos a conseguir un aumento de rendimiento
considerable, adems, si tomamos en cuenta una base de datos que contenga decenas
de Procedimientos Almacenados para uso en una aplicacin orientada a mltiples clientes
al mismo tiempo, un detalle tan sencillo como este puede aportar una diferencia notable.
Se podra usar a modo de ejemplo el siguiente esqueleto:

CREATE PROC dbo.StoreProcedureExample

@variable_1 INT,

@variable_2 VARCHAR(10),

@variable_n BIT

AS

BEGIN

SET NOCOUNT ON;

--INSTRUCCIN 1

--INSTRUCCIN 2

--INSTRUCCIN N

SET NOCOUNT OFF;

END

EVITE USAR SELECT *

Aunque resulte fcil y cmodo usar el comodn (*) para traer todos los campos, este debe
omitirse y en su lugar especificarse los campos que sean necesario traerse. El uso del
comodn impide adems un uso efectivo de forma eficiente de los ndices. En caso de
que sean todos como el uso del * establece, especifique cada uno de ellos, un simple
ALT+F1 sobre el nombre de la tabla seleccionada traer su estructura, por lo que copiar,
pegar los campos y aadir una coma supone un esfuerzo mnimo.
Me he topado en varias ocasiones que al hacer un chequeo de existencia se recurre por
lo regular a la siguiente sintaxis:

IF EXISTS (SELECT * FROM Tabla WHERE Campo=Condicionante)

BEGIN

--INSTRUCCIONES 1,2N

END

Evtelo, no existe ningn motivo para cargar ms trabajo a la base de datos trayendo
todos los campos si lo nico que se desea saber es si existe. Puede sustituirse sin ningn
problema el * por o 1 cumpliendo con su funcin de chequeo de existencia sin necesidad
de traer datos:

IF EXISTS (SELECT '' FROM Tabla WHERE Campo=Condicionante)

BEGIN

--INSTRUCCIONES 1,2N

END

O bien:

IF EXISTS (SELECT 1 FROM Tabla WHERE Campo=Condicionante)

BEGIN

--INSTRUCCIONES 1,2N

END

Ambos le darn la misma funcionalidad sin necesidad de cargar de trabajo adicional e


innecesario a la base de datos.

USE NOLOCK SABIAMENTE

El uso de NOLOCK puede mejorar considerablemente la velocidad de algunas consultas.


Al usar NOLOCK en las consultas se establece que la lectura no atrapa la tabla y esta
puede ser leda al mismo tiempo por todos los usuarios. Sin embargo, hay que tomar en
cuenta que el conjunto de datos mostrados es considerado como una lectura sucia
(http://www.jguru.com/faq/view.jsp?EID=721). Esto significa que los datos mostrados
podran no ser del todo precisos, pudiendo estos encontrarse en medio de alguna
transaccin del tipo DELETE, UPDATE o INSERT. Aun as, para el caso de tablas
estticas como en algunos casos las dedicadas a catlogos fijos y para escenarios de
reportes cuya informacin es ms que nada histrica y que por lo tanto no se ver
afectada en el momento, podra suponer una mejora aceptable sobre todo si son
consultas frecuentes en un ambiente multiusuario.
No se puede usar NOLOCK de forma indiscriminada en todas los Procedimientos
Almacenados, es ms una cuestin de evaluacin sobre escenarios particulares.
NOLOCK se usa al momento de efectuar una consulta y solo aplica a las tablas. Su
estructura seria la siguiente:

SET NOCOUNT ON

SELECT

Campo1,

Campo2,

CampoN

FROM Tabla1 T1(NOLOCK)

INNER JOIN Tabla2 T2(NOLOCK) ON T2.CampoX=T1.CampoX

INNER JOIN Tabla3 T3(NOLOCK) ON T3.CampoY=T2.CampoY

WHERE

<Condicionantes>

SET NOCOUNT OFF

EVITE UTILIZAR EL PREFIJO SP_ EN EL NOMBRE DEL LOS PROCEDIMIENTOS


ALMACENADOS

Utilizar el prefijo sp_ para etiquetar procedimientos almacenados, aunque intuitivo,


puede resultar en una nefasta idea; Por qu? SQL Server reconoce el prefijo sp_
como System Stored Procedure, es decir, un procedimiento almacenado de Sistema.

Este detalle en particular apoya en la forma que SQL Server usa para localizar el
procedimiento almacenado cuando intentamos ejecutarlo, asumiendo que se trata de un
procedimiento almacenado de sistema, y por lo tanto deber estar en la base de
datosMASTER, donde se ubican todos los Procedimientos Almacenados de esta clase.
Intentando primeramente localizar el procedimiento en la base de datos MASTER. Al no
encontrarlo continua su bsqueda la base de datos activa, provocando con esto una cada
del rendimiento que, aunque parezca insignificante en un ambiente de transacciones
pequeo podra influir significantemente si estamos hablando de un ambiente mucho ms
grande en donde se ejecuten miles de transacciones por minuto.

UTILICE SP_EXECUTESQL EN LUGAR EXECUTE

Se recomienda el uso de sp_executesql (http://msdn.microsoft.com/es-


es/library/ms175170(v=sql.105).aspx) , en lugar de la instruccin EXECUTE o EXEC al
ejecutar cdigo dinmico. Esto debido a que sp_executesql admite la sustitucin de
parmetros, es mucho ms verstil que EXECUTE y adems genera planes de
ejecucin; Lo que aumenta las probabilidades de que al volverlo a utilizar resulte ms
eficaz.

EVADA EL USO DE CURSORES

Los cursores son una herramienta usada para acceder y modificar el resultado de una
clusula SELECT fila por fila. El problema con su uso es que consumen una enorme
cantidad de recursos, especialmente de memoria. Siempre que sea posible, se debe
omitir el uso de cursores o minimizar su implementacin. Algunas alternativas y sustitutos
al uso de cursores pueden ser:

Usar ciclos WHILE.


Uso de tablas derivadas (http://msdn.microsoft.com/es-
es/library/aa337504(v=sql.100).aspx).
Uso de subqueries correlacionados
(http://www.dxmaps.com/docsql_e.html#subqueries_correlacionados).
Uso de CASE.
Uso de mltiples consultas.
La combinacin de todas las anteriores.

UTILICE ADECUADAMENTE LAS VARIABLES TIPO TABLA & TABLAS


TEMPORALES

Siempre es mejor usar variables tipo tabla en lugar de tablas temporales?, revisemos las
caractersticas y diferencias entre una y otra.
Variables tipo tabla:

Su uso en procedimientos almacenados provoca menos re compilaciones.
Apuntan a estructuras de memoria por lo que producen menos consumo de
recursos que las tablas temporales.
Su contenido no siempre est en memoria. En caso de que se inserte una
cantidad grande de registros esta se almacena en TEMPDB.
El contenido de la variable no es afectado por el comando ROLLBACK.
No se pueden generar al vuelo.
No usan paralelismo (multiple threads) en su plan de ejecucin. Su uso para tablas
de concurrencia alta o con una gran cantidad de datos puede afectar su desempeo,
siendo este menor en comparacin con una tabla temporal.
No se les puede agregar ndices.
No se les pueden modificar ni truncar una vez creadas.

Tablas temporales:

Se almacenan automticamente en la base de datos TEMPDB.


Su creacin puede ocasionar bloqueos en las tablas sysobjects, sysindexes y
afectar a todo el servidor.
Permite el uso de ndices.
Su uso en procedimientos almacenados puede provocar una re compilacin
continua.
A grandes rasgos, pueden tratarse como una tabla normal.

En general, se puede sugerir el uso de variables tipo tabla siempre que sea posible en
lugar de tablas temporales. Use estas ltimas solo en caso de que se maneje una
cantidad muy grande de informacin y siempre procurando crear su estructura
previamente nunca crendolas al vuelo.

IMPLEMENTACION DE SQL DINAMICO (O CODIGO ROJO)

Aunque en general el uso de SQL Dinmico esta algo condenado debido a que una mala
implementacin puede resultar en un grieta de seguridad que de entrada a un severo
caso de SQL Injection(http://www.websec.mx/blog/ver/Referencia_para_Inyeccion_SQL).
La implementacin que se sugiere esta encapsulada dentro de un procedimiento
almacenado, no es del todo dinmica para la aplicacin cliente, es decir, no le permite
estructurar sentencias libres y est orientada ms que nada a procedimientos
almacenados cuya funcin principal es una consulta parametrizada con opciones
variables. Usare la base de datos AdventureWorksDW2008R2 para ilustrar un ejemplo.
Probaremos con el siguiente procedimiento almacenado:

CREATE PROCEDURE BeforeRedCodeSELECT

@ProductKey INT=NULL,

@SalesOrderLineNumber TINYINT=NULL,

@OrderQuantity SMALLINT=NULL,

@CurrencyKey INT=NULL

AS

BEGIN

SET NOCOUNT ON

SELECT

FIS.ProductKey,

FIS.OrderQuantity,

FIS.UnitPrice,

FIS.SalesOrderNumber,

FIS.CurrencyKey,

FIS.SalesOrderLineNumber

FROM FactInternetSales FIS(NOLOCK)

WHERE FIS.ProductKey=ISNULL(@ProductKey, FIS.ProductKey)

AND FIS.SalesOrderLineNumber= ISNULL (@SalesOrderLineNumber, FIS.SalesOr


derLineNumber)

AND FIS.OrderQuantity= ISNULL (@OrderQuantity, FIS.OrderQuantity)

AND FIS.CurrencyKey= ISNULL (@CurrencyKey, FIS.CurrencyKey)

SET NOCOUNT OFF

END
En este caso la consulta acepta valores nulos y se usa ISNULL (se puede
usar COALESCE en su lugar, pero para razones prcticas en el ejemplo no tiene mucho
caso y ISNULL es un poco ms rpido que COALESCE) para dar la flexibilidad al
procedimiento almacenado y poder consultar ya sea usando uno, los cuatro o cualquier
combinacin de parmetros. Ahora, convirtiendo la consulta en SQL Dinmico, quedara
de la siguiente forma:
CREATE PROCEDURE AfterRedCodeSELECT

@ProductKey INT=NULL,

@SalesOrderLineNumber TINYINT=NULL,

@OrderQuantity SMALLINT=NULL,

@CurrencyKey INT=NULL

AS

BEGIN

SET NOCOUNT ON

DECLARE @Query AS NVARCHAR(MAX)

SET @Query=N'

SELECT

FIS.ProductKey,

FIS.OrderQuantity,

FIS.UnitPrice,

FIS.SalesOrderNumber,

FIS.CurrencyKey,

FIS.SalesOrderLineNumber

FROM FactInternetSales FIS(NOLOCK)

WHERE 1=1 '

IF @ProductKey IS NOT NULL

BEGIN

SET @Query = @Query + ' AND FIS.ProductKey = @ProductKey '

END
IF @SalesOrderLineNumber IS NOT NULL

BEGIN

SET @Query = @Query + ' AND FIS.SalesOrderNumber = @SalesOrderLineNumber '

END

IF @OrderQuantity IS NOT NULL

BEGIN

SET @Query = @Query + ' AND FIS.OrderQuantity = @OrderQuantity '

END

IF @CurrencyKey IS NOT NULL

BEGIN

SET @Query = @Query + ' AND FIS.CurrencyKey =@CurrencyKey '

END

--PRINT @Query

EXECUTE sp_executesql @Query

, N'@ProductKey INT, @SalesOrderLineNumber TINYINT, @OrderQuantity SMALLINT,


@CurrencyKey INT '

, @ProductKey = @ProductKey

, @SalesOrderLineNumber = @SalesOrderLineNumber

, @OrderQuantity = @OrderQuantity

, @CurrencyKey = @CurrencyKey

SET NOCOUNT OFF

END

Aqu el procedimiento almacenado solo incluir las condiciones que sean necesarias y
que no tengan un valor nulo. Ambos procedimientos devuelven los mismos resultados y
son equivalentes el uno al otro. Usando SQL Profiler, podemos apreciar la diferencia en
la optimizacin de recursos:
Las lecturas se reducen para este escenario particular, en poco ms de un 80%, la
duracin en un 60% y en el uso del CPU, un cambio total.

Por lo que se aprecia el SQL Dinmico podra no ser tan malo como parece y puede
ofrecernos alternativas creativas para optimizar nuestras consultas. Evalu sus consultas
complejas y pesadas para sopesar si una refactorizacin usando SQL Dinmico puede
ayudarle, cuidando siempre que esta no se preste para casos de SQL Injection.

APRENDA A APLICAR INDICES DE FORMA OPTIMIZADA

Es ya bien conocido el uso de ndices para acelerar consultas en SQL Server y otros
motores de bases de datos. Estos le indican al motor de la base de datos en turno, que
esa columna o conjunto de columnas se van a usar con mayor frecuencia, lo cual hace
que los datos se almacenen en memoria para contar con un acceso ms eficiente a los
datos.

Es aconsejable que los ndices se formen sobre los campos que


encuentren frecuentemente en alguno de los siguientes escenarios:
Son PRIMARY KEY o FOREIGN KEY.
Se usan frecuentemente para enlazar tablas con JOIN.
Se usan de forma habitual dentro de los procedimientos almacenados o en
consultas emparejados con alguno de los siguientes comandos: BETWEEN, ORDER
BY, GROUP BY, TOP, DISTINCT (Aunque ya mencionamos que estos deben de evitarse
en lo posible, si no hay otra alternativa, hay que considerar sus campos para la aplicacin
de ndices).
Son de uso corriente para filtrados en la clausura WHERE.

Pese a que los ndices generen una mejora en tiempos de respuesta, tampoco se puede
abusar indiscriminadamente de ellos. En contraposicin a su mejora de tiempo de
respuesta cada ndice ocupa un espacio equivalente al nmero de registros en la tabla,
penalizando el rendimiento en la base de datos al
ejecutar INSERT, UPDATES y DELETE.
Omita crear ndices con campos que contienen pocos valores, como aquellos que son de
tipo BIT o CHAR (de uno a cuatro) y asegrese de no agregar el mismo ndice con
distintos nombres.
El estudio profundo de la creacin, diseo y mantenimiento de ndices escapa del alcance
de este articulo (espero escribir algo ms elaborado sobre el tema de ndices en un
futuro), sin embargo es importante que se tome el tiempo para comprenderlos. Su
correcta aplicacin puede favorecer por mucho el tiempo de respuesta de sus consultas.

RECOMENDACIONES ADICIONALES

Si el procedimiento almacenado contiene demasiadas sentencias IF-ELSE para


ejecutar distintos procesos acorde a ciertos parmetros, resulta ms eficiente separar
cada bloque y encapsularlos en procedimientos almacenados diferentes; Esto para evitar
que el plan de ejecucin cambie acorde al valor del resultado de la expresin IF,
desperdiciando con esto la ventaja de el plan de ejecucin en memoria y la pre
compilacin.
Evite el uso de la instruccin SELECT-INTO. Al ejecutarse se bloquean las tablas
involucradas. Aplique en su lugar la sentencia INSERT INTO-SELECT.
Trate de usar siempre la instruccin JOIN antes que cualquier sub consulta.
Use la instruccin BETWEEN en lugar de IN siempre que sea posible.
En el caso de que se use la instruccin LIKE, evite el uso del comodn % al inicio
de la cadena a buscar. Esto debido a que si se aplica, la bsqueda tendra que leer todos
los datos de la tabla o tablas involucradas para responder a la consulta. Se recomienda
que existan al menos tres caracteres antes del comodn.
Evite en la medida de lo posible el uso de DISTINCT
En caso de usar la instruccin UNION y existiera la seguridad de que en los
SELECT involucrados no se obtendrn registros duplicados, entonces lo recomendable en
este escenario es sustituir UNION por UNION ALL para evitar que se haga uso implcito
de la instruccin DISTINCT, ya que esta aumenta el consumo de recursos.
Evada siempre que sea posible el uso de ORDER BY. Al igual
que DISTINCT consume una elevada cantidad de recursos. Considere si es realmente
necesario usarlo o, si por otro lado se puede dejar el ordenamiento de los resultados a la
aplicacin que recibir los datos.
Use SQL Profiler y levante una traza para estudiar el rendimiento de los
procedimientos almacenados de mayor concurrencia y vea opciones para refactorizar el
proceso. Otorgue especial atencin a las columnas CPU, Duraction,reads y writes para
optimizar el uso de memoria y el I/O generado.
Mantenga las transacciones lo ms cortas posibles dentro de un procedimiento
almacenado. Esto favorece a la reduccin del nmero de bloqueos, promoviendo a
acelerar el rendimiento general de la base de datos.
Utilice el Plan de Ejecucin para revisar sus consultas, entienda como SQL
Server est ejecutando sus consultas y evalu sus sugerencias.
Si requiere almacenar volmenes de texto muy grandes, pero son menores a 8000
caracteres, use el tipo de datoVARCHAR en lugar de TEXT.
Evalu cuidadosamente el uso de CHAR y VARCHAR dependiendo si el campo
en el que se va a usar vara mucho o no de tamao. Esto para sopesar rendimiento de
velocidad sobre rendimiento de almacenamiento. SQL Server procesa ms rpido las
columnas de longitud fija. Use CHAR para columnas de poca variacin en longitud
y VARCHAR para aquellas que no tienen una longitud estable o promedio.
No use columnas con tipos de datos FLOAT, REAL o
DATETIME como FOREIGN KEY.

RESUMEN

Los puntos presentados en este artculo sirven como esquema general para la
optimizacin de consultas y procedimientos almacenados, pero, cabe aclarar que la
optimizacin es ms bien un asunto de entornos particulares. Quiz lo ideal sera
establecer estas recomendaciones desde el inicio de un desarrollo, si el desarrollo ya est
en produccin lo ms coherente es evaluar qu puntos pueden o no aplicarse. Menciono
esto ltimo por escenarios en los que, por ejemplo, ya se efectu todo el desarrollo de X
aplicacin; Algunos programadores usan el retorno del conteo de las filas afectadas para
validar que el procedimiento almacenado efectivamente se ejecut y en base a eso la
aplicacin sigue o no adelante (esto no debera ser as, pero pasa con mayor frecuencia
de la que uno espera). En este escenario, aplicar SET NOCOUNT ON podra generar
muchos errores, en especial si la aplicacin es grande y compleja y la cantidad de
procedimientos almacenados a los que se les debe aplicar tambin es alta. El uso
indiscriminado de NOLOCK en las tablas puede crear tambin problemas, sobre todo si la
aplicacin debe mostrar informacin consistente y se aplica esta prctica a
procedimientos que deben de respetar ciertas transacciones antes de efectuar cambios.
En conclusin, la optimizacin es un trabajo ms que nada artesanal y depende en
muchos casos de en qu punto (inicio, mitad o fase final del desarrollo) decidamos
aplicarla.

You might also like