Sei sulla pagina 1di 5

Optimizacin de Procedimientos Almacenados con Tablas Temporarias

by Luis Martin

Una de las tareas ms complicadas en el trabajo de optimizacin, es la de encontrar ndices que


mejoren la performance de un software de aplicaciones de terceros.
El problema nace en que no estamos habilitados a modificar ninguna lnea del cdigo original y, por
lo tanto, nuestra funcin se limita a encontrar mejoras en los ndices y en las tareas de
mantenimiento.
Cuando la consulta que queremos optimizar es del tipo simple, es decir, sin llamadas a
procedimientos almacenados y sin contener tablas auxiliares, nuestro trabajo se simplifica ya que,
en forma inmediata, podemos observar el plan de ejecucin y generar conclusiones sobre la
posibilidad de mejorar su ejecucin.
Pero cuando la consulta que nos preocupa es un procedimiento almacenado que llama a otros
procedimientos almacenados y que, adems, contiene tablas auxiliares, nuestro trabajo se complica.
Ni el Analizador de Consultas ni el Optimizador de ndices pueden entender procedimientos
almacenados con tablas auxiliares ni con llamadas a otros procedimientos almacenados.

Mtodo de Trabajo
Supongamos que utilizando el Profiler nos encontramos con el siguiente caso:

La lnea pintada de azul es la ejecucin de una parte del procedimiento almacenado que, como se
observa, toma mucho tiempo y adicionalmente realiza muchas lecturas.
En la ventana inferior el Profiler muestra la instruccin de SQL ejecutada.
Como vemos comienza con un INSERT INTO #CUENTASSALDOS. Esto implica generar una
tabla temporaria, pero no es la parte que consume tiempo (la insercin) por el contrario el problema
comienza con el SELECT. Por lo tanto pintamos desde el SELECT hasta el final y la copiamos al
Analizador de Consultas.
Una vez copiada y tcnicamente ordenada, la consultas es la siguiente:
SELECT AR.CODCUE,
null,
null,

SUM(L.DEBE),
SUM(L.HABER),
sum(CASE WHEN L.Debe <> 0 THEN L.Cantidad
ELSE L.Cantidad * -1 END),
0,
0
FROM #CUENTASARBOL2 AR (NOLOCK)
JOIN Linasientos L (nolock)
ON (AR.codcue = L.codcue)
JOIN Transac T (nolock)
ON L.Nrotrans = T.Nrotrans
JOIN Cmpasociados Ca (nolock)
ON (L.Nrotrans = Ca.Nrotrans
AND isnull (L.Nrosdb , 0) = isnull (Ca.Nrosdb , 0))
LEFT OUTER JOIN Subdiarios Sd (nolock)
ON (isnull (L.Nrosdb , 0) = isnull (Sd.Nrosdb , 0))
JOIN #AuxEmpSuc AES (nolock)
ON t.codemp = AES.Codemp
AND t.Codsuc = AES.Codsuc
JOIN #AuxCmps AC (nolock)
ON Ca.Codcmp = AC.Codcmp
WHERE T.Nrotranselim is null
AND T.Fecha <= '2/28/2005'
AND (Ca.Nroasires is null
OR (Sd.Resume = 0
AND AC.codcmp is not null))

GROUP BY AR.Codcue
Como podemos observar, tenemos 3 tablas auxiliares: #CUENTASARBOL2, #AuxEmpSuc y
#AuxCmps.
Esta consulta no genera plan de ejecucin alguno. La nica forma es crear las 3 tablas con otro
nombre, reemplazar la consulta con los nuevos nombres y estaremos en condiciones para obtener el
plan de ejecucin y ejecutar el Optimizador de ndices.
Es evidente que, para construir las tablas, necesitamos conocer cules son sus columnas y sus tipos
de datos.
En el caso de las tablas #AuxEmpSuc y #AuxCmps no resulta complicado, dado que en la misma
consulta se encuentran las columnas que necesita como: Codsuc y Codcmp y basta ingresar en el
Administrador Corporativo para encontrar las caractersticas de esas columnas.
Otra forma de encontrar las caractersticas de las tablas temporarias es analizar el procedimiento
almacenado. Buscamos en el cdigo la creacin de las tres tablas que nos preocupan.
El caso de #CUENTASARBOL2, se complica ya que la consulta no ayuda demasiado. Pero si
observamos cuidadosamente el trazado generado por el Profiler,

podemos encontrar el la lnea pintada de azul, la instruccin que crea la tabla temporaria
#CUENTASARBOL2.
Todos los campos pueden encontrarse en la base de datos y por lo tanto podemos crear la ltima
tabla que nos faltaba.
Una vez creadas las tablas (con nombre libre y con el cuidado de no crearlas con nombres
existentes) nos queda la siguiente consulta:
SELECT AR.CODCUE,
null,
null,
SUM(L.DEBE),
SUM(L.HABER),
sum(CASE WHEN L.Debe <> 0 THEN L.Cantidad
ELSE L.Cantidad * -1 END),
0,
0

FROM CUENTASARBOL2_Luis AR (NOLOCK)


JOIN Linasientos L (nolock)
ON (AR.codcue = L.codcue)
JOIN Transac T (nolock)
ON L.Nrotrans = T.Nrotrans
JOIN Cmpasociados Ca (nolock)
ON (L.Nrotrans = Ca.Nrotrans
AND isnull (L.Nrosdb , 0) = isnull (Ca.Nrosdb , 0))
LEFT OUTER JOIN Subdiarios Sd (nolock)
ON (isnull (L.Nrosdb , 0) = isnull (Sd.Nrosdb , 0))
JOIN AuxEmpSuc_luis AES (nolock)
ON t.codemp = AES.Codemp
AND t.Codsuc = AES.Codsuc
JOIN AuxCmps_luis AC (nolock)
ON Ca.Codcmp = AC.Codcmp
WHERE T.Nrotranselim is null
AND T.Fecha <= '2/28/2005'
AND (Ca.Nroasires is null
OR (Sd.Resume = 0
AND AC.codcmp is not null))
GROUP BY AR.Codcue
El plan de ejecucin de la consulta anterior es:

En este plan de ejecucin podemos ver algunos costos altos como en las tablas TRANSAC y
LINASIENTOS.
Si ejecutamos en Optimizador de ndices nos sugiere un nico ndice que es el siguiente:
CREATE NONCLUSTERED INDEX

[IXC_TRANSAC_NroTranselim_CodEmp_Fecha_CodSuc_NroTrans] ON [dbo].[TRANSAC]
(
[NROTRANSELIM] ASC,
[CODEMP] ASC,
[FECHA] ASC,
[CODSUC] ASC,
[NROTRANS] ASC
)
Creamos este nuevo ndice y observemos el siguiente plan de ejecucin.

El costo total de ejecucin es cero. Lo mismo ocurre con los costos de las tablas TRANSAC y
LINASIENTOS.
Al ejecutar nuevamente el proceso, observemos la traza obtenida en el Profiler.

La aplicacin del nuevo ndice ha dado como resultado que el tiempo de ejecucin se redujo a
menos de la mitad.
No siempre se obtienen valores de costos cero, pero seguramente este tipo de procesos complejos
que consumen recursos y tiempo, son los cuales debemos prestar mayor atencin en la difcil tarea
de optimizar.

Potrebbero piacerti anche