Consultas con Exists traicioneras....

El otro día analizando un tkproff de una SQL me encontré con los siguientes tiempos, la consulta no era ni mensual, era de unos 20 días aproximadamente

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.84       0.84          0          4          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch     6103    108.37     494.38     304213    3173248          0       91530
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     6105    109.21     495.23     304213    3173252          0       91530

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 374  
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
     91530      91530      91530  VIEW  (cr=3173248 pr=304213 pw=0 time=94078182 us starts=1 cost=12239 size=670 card=2)

Como podemos observar, el tiempo que tarda en dar respuesta son casi 2 minutos aproximadamente con más de 3.000.000 de bloques leídos.

Si seguimos analizando el árbol de ejecución vemos algo muy extraño

Ambas operaciones NESTED LOOPS son las que están causando la penalización a la hora de obtener buenos tiempos en la ejecución, además ambas operaciones tienen un punto en común, que es CFR_IN_001.

¿Qué tiene la consulta para que este haciendo ese acceso incorrecto? Pues dos clausulas EXISTS usadas erróneamente.

Hay que tener claro una cosa, cuando nosotros tenemos una SELECT que tiene una subconsulta como es nuestro caso, si es una subconsulta "IN" y tenemos que el grueso de la ejecución se lo lleva la SELECT y la subconsulta "IN", tiene poco peso, es decir, que es una consulta ligera, vamos a tener mejor rendimiento utilizando "IN" que utilizando "EXISTS", porque "EXISTS" me va dar un mejor beneficio cuando el grueso de "EXISTS" sea muy grande respecto a una ejecución ligera de la consulta principal.

En este caso, no es necesario aplicar "IN" or "EXISTS", por como esta definida la SQL, con un join debería ser suficiente.

Ejecutamos ahora la consulta, con estos tiempos

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        2      0.37       0.37          0          0          0           0
Execute      2      0.00       0.00          0          0          0           0
Fetch     7081      8.81       9.24        768    1392473          0      106172
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     7085      9.19       9.61        768    1392473          0      106172

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 374  
Number of plan statistics captured: 2

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         0      53086     106172  VIEW  (cr=696236 pr=384 pw=0 time=1814900 us starts=1 cost=536 size=670 card=2)

La ejecución es inmediata y las operaciones NESTED LOOP ya tienen un tiempo normal

En el siguiente capítulo abordaremos como reducir el número de bloques leídos.