En el artículo anterior vimos que era ventajoso descomponer las etapas de análisis de un documento XML grande, para trabajar en cada una de ellas una sola vez. Pudimos pasar de un proceso que no terminaba nunca porque llenaba la tempdb, a otro que tardaba hora y media. Puede parecer un cambio dramático, pero aún así es mucho tiempo. ¿Podemos mejorarlo?
Nueva etapa
Como hemos dicho en la introducción, vamos a ver si podemos descomponer aún más la tarea total. Para añadir una etapa por cada proceso que podamos separar, vamos a emplear en esta ocasión un cursor. Podríamos hacerlo igualmente con otra tabla temporal intermedia, pero vamos a usar una variable XML para ver su comportamiento. Vamos a analizar tanto el tiempo, como los accesos a memoria, como el plan que crea el optimador de consultas.
Para lograr esto, primero obtenemos el plan en caché desde la memoria en formato XML:
De esta forma nos estamos asegurando que el acceso a los planes cacheados y la conversión en formato XML se hace una sola vez. Aunque parezca extraño, luego vamos a ver la importancia capital de esto.
Si analizamos el plan de esta parte, vemos que es prácticamente igual al que vimos en el artículo anterior, no hay nada que nos indique una mejora:
Búsqueda en XML
Una vez que tenemos definido el cursor y tenemos los datos en la variable XML, lo que hacemos es procesar individualmente cada plan y añadir el resultado a la tabla temporal. La consulta es muy parecida a la del artículo anterior:
Si miramos el plan de ejecución de esta parte, vamos a ver que es idéntico al del artículo anterior, incluso si miramos las filas procesadas, el número de veces que se ejecuta un bucle, las filas resultantes, etc., el plan es idéntico:
Análisis del plan
Entonces, ¿cómo hemos podido bajar el tiempo desde hora y media a diez minutos? Alguna diferencia tiene que haber. En este artículo y en los anteriores hemos ido señalando la diferencia: hacer las tareas costosas una solo vez. Porque si miramos en cualquiera de los operadores XML Reader with XPath filter o XML Reader, vemos que actúan sobre la variable @PlanXml xml en vez de sobre la función FNGETQUERYPLAN.[query_plan]. Es decir, los planes cacheados se traen, convierten y procesan una sola vez, en vez de una vez por cada referencia. Ahí está el secreto de que, al separar los procesos, el resultado final sea mucho más rápido, desde luego no en que lo hagamos con un cursor como exposición:
Por lo tanto debemos tener mucho cuidado, no sólo en este caso, sino siempre, en cómo se usan las funciones dentro de una consulta, si se llaman una vez, una vez por fila o varias veces por fila.
Esto hace mucha diferencia en cualquier consulta. Por ejemplo, cambiar –cuando se puede– una función escalar que se llama por fila, por una función de tabla que devuelve todo a la vez, puede hacer que la consulta tarde la décima parte.
Estadísticas de E/S
Si miramos la forma en que la consulta del artículo anterior trabaja con la memoria y cómo trabaja esta, veremos de nuevo el cambio tan dramático. Para uno de los planes cacheados grandes, estos serían los datos de la consulta anterior:
Comparemos las lecturas de objetos grandes:
La diferencia es casi de 50 a 1, sin comentarios.
CONCLUSIÓN
En este artículo hemos visto, en definitiva, que cuando se trata de trabajar con funciones tenemos que verificar que se usen el mínimo de veces posibles y hemos vuelto a ver la ventaja que supone que nosotros cacheemos los datos cuando SQL Server no lo hace.
En el próximo artículo veremos más a fondo la diferencia que hay entre documentos XML grandes y pequeños y por qué trabaja el optimador de consultas como lo hace.
Más Información
Desde Danysoft y si rellenas este formulario, te ayudaremos a facilitarte la información que necesitas.
Dejar un comentario
¿Quieres unirte a la conversación?¡Siéntete libre de contribuir!