SQL

16
Optimización SQL en Oracle 296 http://www.optimizacionsqlenoracle.com Funciones analíticas Antes de empezar a leer este capítulo, plantéese una consulta SQL muy simple. ¿Cómo consultar los tres trabajadores que más cobran? Es una pregunta sencilla, pero tómese su tiempo antes de seguir leyendo. En una tabla que contenga los empleados y sus salarios, como la tabla EMP del usuario de ejemplo SCOTT, debería ser algo tan sencillo como lo siguiente: SQL> select ename, sal, rownum top 2 from (select ename, sal from scott.emp order by sal desc) e 3 where rownum<=3; ENAME SAL TOP ---------- ---------- ---------- KING 5000 1 SCOTT 3000 2 FORD 3000 3 En la subconsulta de la cláusula FROM el salario se ordena de mayor a menor y se filtran solo las primeras filas que devuelva la consulta. Sin embargo, esta consulta no es del todo correcta. ¿Qué pasaría si hubiera otro trabajador que cobrase 3.000? No

Transcript of SQL

  • Optimizacin SQL en Oracle

    296 http://www.optimizacionsqlenoracle.com

    Funciones analticas Antes de empezar a leer este captulo, plantese una consulta SQL muy simple. Cmo consultar los tres trabajadores que ms cobran? Es una pregunta sencilla, pero tmese su tiempo antes de seguir leyendo. En una tabla que contenga los empleados y sus salarios, como la tabla EMP del usuario de ejemplo SCOTT, debera ser algo tan sencillo como lo siguiente: SQL> select ename, sal, rownum top

    2 from (select ename, sal from scott.emp order by sal desc) e

    3 where rownum

  • Mejora de SQL: casos y usos

    http://www.optimizacionsqlenoracle.com 297

    aparecera en la consulta, como tampoco aparecera el tercer sueldo mayor como correspondera para el clculo manejando empates, es decir, lo que se denomina en estadstica el ranking denso o dense rank.

    Llegados a este punto, los programadores tienen que ajustar la consulta de la siguiente forma para que los datos que devuelva sean fiables.

    SQL> select ename, sal, 1 top

    2 from scott.emp

    3 where sal in (select max(sal) from scott.emp)

    4 union all

    5 select ename, sal, 2 top

    6 from scott.emp

    7 where sal in (select max(sal) from scott.emp

    8 where sal not in (select max(sal) from scott.emp))

    9 union all

    10 select ename, sal, 3 top

    11 from scott.emp

    12 where sal in (select max(sal) from scott.emp

    13 where sal not in (select max(sal) from scott.emp)

    14 and sal not in (select max(sal) from scott.emp

    15 where sal not in (select max(sal) from scott.emp)));

    ENAME SAL TOP

    ---------- ---------- ----------

    KING 5000 1

    SCOTT 3000 2

    FORD 3000 2

    JONES 2975 3

    Es decir, obtener primero los trabajadores con salario mximo, luego los trabajadores con salario mximo excluyendo los primeros, y unirlo finalmente a los trabajadores con salario mximo excluyendo todos los anteriores. En un vistazo al plan de ejecucin, el cmputo total de accesos a la tabla EMP es considerable. ----------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)|

    ----------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 3 | 30 | 30 (80)|

    | 1 | UNION-ALL | | | | |

    |* 2 | TABLE ACCESS FULL | EMP | 1 | 10 | 3 (0)|

    | 3 | SORT AGGREGATE | | 1 | 4 | |

    | 4 | TABLE ACCESS FULL | EMP | 14 | 56 | 3 (0)|

    |* 5 | TABLE ACCESS FULL | EMP | 1 | 10 | 3 (0)|

    | 6 | SORT AGGREGATE | | 1 | 4 | |

    |* 7 | TABLE ACCESS FULL | EMP | 13 | 52 | 3 (0)|

    | 8 | SORT AGGREGATE | | 1 | 4 | |

    | 9 | TABLE ACCESS FULL | EMP | 14 | 56 | 3 (0)|

    |* 10 | TABLE ACCESS FULL | EMP | 1 | 10 | 3 (0)|

    | 11 | SORT AGGREGATE | | 1 | 4 | |

    |* 12 | TABLE ACCESS FULL | EMP | 12 | 48 | 3 (0)|

    | 13 | SORT AGGREGATE | | 1 | 4 | |

    | 14 | TABLE ACCESS FULL | EMP | 14 | 56 | 3 (0)|

    | 15 | SORT AGGREGATE | | 1 | 4 | |

    |* 16 | TABLE ACCESS FULL | EMP | 13 | 52 | 3 (0)|

    | 17 | SORT AGGREGATE | | 1 | 4 | |

    | 18 | TABLE ACCESS FULL| EMP | 14 | 56 | 3 (0)|

    ----------------------------------------------------------------------

  • Optimizacin SQL en Oracle

    298 http://www.optimizacionsqlenoracle.com

    Si en lugar de los tres primeros fuesen los cinco primeros, o los diez primeros, el volumen de la sentencia aumentara exponencialmente, y cmo podra modificarse la sentencia para que mostrase los tres primeros de cada departamento o de cada oficio? Al final, este tipo de sentencias terminan creciendo tanto que son prcticamente inmanejables.

    Otro escenario similar podra ser el siguiente: cmo consultar los empleados que ganan ms de la media de su oficio o de su departamento?

    SQL> select ename, sal, deptno

    2 from scott.emp e

    3 where sal > (select avg(sal)

    4 from scott.emp d

    5 where e.deptno=d.deptno)

    6 or sal > (select avg(sal)

    7 from scott.emp d

    8 where e.job=d.job);

    ENAME SAL DEPTNO

    ---------- ---------- ----------

    ALLEN 1600 30

    JONES 2975 20

    BLAKE 2850 30

    SCOTT 3000 20

    KING 5000 10

    TURNER 1500 30

    ADAMS 1100 20

    FORD 3000 20

    MILLER 1300 10

    9 filas seleccionadas.

    De este modo, si queremos visualizar el dato de la media de departamentos y de oficios es preciso usar subconsultas correlacionadas en la clusula SELECT, accediendo por cada una de las filas de resultado dos veces a la tabla EMP en su totalidad para visualizar la media del oficio y el departamento seleccionados.

    SQL> select ename, sal, deptno,

    2 (select avg(sal)

    3 from scott.emp

    4 where job=e.job) media_oficio,

    5 (select avg(sal)

    6 from scott.emp

    7 where deptno=e.deptno) media_deptno

    8 from scott.emp e

    9 where sal > (select avg(sal)

    10 from scott.emp d

    11 where e.deptno=d.deptno)

    12 or sal > (select avg(sal)

    13 from scott.emp d

    14 where e.job=d.job);

  • Mejora de SQL: casos y usos

    http://www.optimizacionsqlenoracle.com 299

    ENAME SAL DEPTNO MEDIA_OFICIO MEDIA_DEPTNO

    ---------- ---------- ---------- ------------ ------------

    ALLEN 1600 30 1400 1566,66667

    JONES 2975 20 2758,33333 2175

    BLAKE 2850 30 2758,33333 1566,66667

    SCOTT 3000 20 3000 2175

    KING 5000 10 5000 2916,66667

    TURNER 1500 30 1400 1566,66667

    ADAMS 1100 20 1037,5 2175

    FORD 3000 20 3000 2175

    MILLER 1300 10 1037,5 2916,66667

    9 filas seleccionadas.

    El plan de ejecucin muestra que, tambin en este caso, se est accediendo un alto nmero de veces a la tabla EMP completa.

    -----------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)|

    -----------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 1 | 21 | 3 (0)|

    | 1 | SORT AGGREGATE | | 1 | 12 | |

    |* 2 | TABLE ACCESS FULL | EMP | 3 | 36 | 3 (0)|

    | 3 | SORT AGGREGATE | | 1 | 7 | |

    |* 4 | TABLE ACCESS FULL | EMP | 5 | 35 | 3 (0)|

    |* 5 | FILTER | | | | |

    | 6 | TABLE ACCESS FULL | EMP | 14 | 294 | 3 (0)|

    | 7 | SORT AGGREGATE | | 1 | 7 | |

    |* 8 | TABLE ACCESS FULL| EMP | 5 | 35 | 3 (0)|

    | 9 | SORT AGGREGATE | | 1 | 12 | |

    |* 10 | TABLE ACCESS FULL| EMP | 3 | 36 | 3 (0)|

    -----------------------------------------------------------------

    Las funciones analticas proporcionan la capacidad de organizar grupos con las filas que se estn procesando y mantener esa ventana de resultados disponible para la generacin de las filas finales. De este modo, el primer ejemplo del captulo, en el que se pretenda visualizar los empleados en ranking para obtener los tres primeros por salario, sera tan sencillo como: SQL> select ename, deptno, job, sal, RANKING

    2 from (select e.*, dense_rank() over (order by sal desc) RANKING

    3 from scott.emp e) e

    4 where ranking

  • Optimizacin SQL en Oracle

    300 http://www.optimizacionsqlenoracle.com

    ----------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)|

    ----------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 14 | 728 | 4 (25)|

    |* 1 | VIEW | | 14 | 728 | 4 (25)|

    |* 2 | WINDOW SORT PUSHED RANK| | 14 | 294 | 4 (25)|

    | 3 | TABLE ACCESS FULL | EMP | 14 | 294 | 3 (0)|

    ----------------------------------------------------------------------

    En este caso, la tabla EMP solo es accedida una vez y direccionada a una ventana WINDOW SORT PUSHED RANK que clasifica las filas por orden de salario, de mayor a menor, para asignarles un ranking una vez se lean todas de la tabla EMP.

    La sintaxis de las funciones analticas es la siguiente:

    FUNCION( ,, )

    OVER ( )

    De modo que la funcin define, para las columnas seleccionadas, un rea intermedia clasificada en particiones donde se aplica una ordenacin sobre unas columnas en concreto.

    En el caso anterior, usando la funcin DENSE_RANK() sobre el total de los registros, el criterio de ordenacin ORDER BY SAL DESC ordena los registros de mayor a menor salario y, una vez completado el proceso de ordenacin, cada fila recibe una puntuacin basada en el ranking y contemplando el empate como una sola clasificacin.

    Del mismo modo, se podra haber usado RANK(). La diferencia est en que as los empates cuentan como posicin, es decir, si hay dos elementos en el ranking 2, el siguiente elemento tiene ranking 4.

    La clusula PARTITION BY en las funciones analticas establece particiones en la ventana de memoria para clasificar las filas, de modo que los valores se agrupan y ordenan en funcin de las columnas definidas. Esto permitira, por ejemplo, establecer el ranking de salarios previo por departamentos en una sola lectura a la tabla EMP de la siguiente forma:

    SQL> select ename, deptno, job, sal, RANKING

    2 from (select e.*, dense_rank() over (PARTITION BY deptno

    3 order by sal desc) RANKING

    4 from scott.emp e) e

    5 where ranking

  • Mejora de SQL: casos y usos

    http://www.optimizacionsqlenoracle.com 301

    ENAME DEPTNO JOB SAL RANKING

    ---------- ---------- --------- ---------- ----------

    KING 10 PRESIDENT 5000 1

    CLARK 10 MANAGER 2450 2

    MILLER 10 CLERK 1300 3

    SCOTT 20 ANALYST 3000 1

    FORD 20 ANALYST 3000 1

    JONES 20 MANAGER 2975 2

    ADAMS 20 CLERK 1100 3

    BLAKE 30 MANAGER 2850 1

    ALLEN 30 SALESMAN 1600 2

    TURNER 30 SALESMAN 1500 3

    10 filas seleccionadas.

    ----------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)|

    ----------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 14 | 728 | 4 (25)|

    |* 1 | VIEW | | 14 | 728 | 4 (25)|

    |* 2 | WINDOW SORT PUSHED RANK| | 14 | 294 | 4 (25)|

    | 3 | TABLE ACCESS FULL | EMP | 14 | 294 | 3 (0)|

    ----------------------------------------------------------------------

    Para el ejemplo del listado de empleados con los datos complementarios de salario medio de su oficio y de su departamento, la funcin analtica AVG clasificar las medias para cada particin definida. SQL> select ename, sal, deptno,

    2 AVG(sal) over (partition by job) media_oficio,

    3 AVG(sal) over (partition by deptno) media_deptno

    4 from scott.emp e;

    ENAME SAL DEPTNO MEDIA_OFICIO MEDIA_DEPTNO

    ---------- ---------- ---------- ------------ ------------

    SCOTT 3000 20 3000 2175

    FORD 3000 20 3000 2175

    MILLER 1300 10 1037,5 2916,66667

    SMITH 800 20 1037,5 2175

    JAMES 950 30 1037,5 1566,66667

    ADAMS 1100 20 1037,5 2175

    CLARK 2450 10 2758,33333 2916,66667

    BLAKE 2850 30 2758,33333 1566,66667

    JONES 2975 20 2758,33333 2175

    KING 5000 10 5000 2916,66667

    MARTIN 1250 30 1400 1566,66667

    WARD 1250 30 1400 1566,66667

    TURNER 1500 30 1400 1566,66667

    ALLEN 1600 30 1400 1566,66667

    14 filas seleccionadas.

    Dado que las funciones analticas se comportan como una funcin de agrupacin previa a la visualizacin de resultados y posterior a la carga de filas, no es posible utilizarlas en la clusula WHERE. Lgicamente, al aplicar un filtro para cada fila para aceptarla o rechazarla en el conjunto total de registros, no est disponible an

  • Optimizacin SQL en Oracle

    302 http://www.optimizacionsqlenoracle.com

    el valor total final de media aritmtica de estos. El proceso de tratamiento de las funciones analticas es el siguiente:

    - Carga de las filas en funcin de los filtros y combinaciones definidos en la

    ventana de clculo previa.

    - Clasificacin de las filas en las particiones definidas por PARTITION BY.

    - Ordenacin opcional de las filas. Para las funciones MAX, MIN y AVG no tiene sentido tratar los valores ordenados tal y como sucede en el ranking.

    - Aplicacin de la funcin analtica al grupo de filas.

    La forma ptima de resolver la consulta de empleados que cobran ms que la media de su oficio o que la media de su departamento sera aplicar la misma estrategia que con el uso de RANK() y DENSE_RANK como subconsulta en la clusula FROM. De este modo, el resultado de las medias se manejar como una vista que contiene columnas adicionales con estos clculos.

    SQL> select *

    2 from (select ename, sal, deptno,

    3 AVG(sal) over (partition by job) media_oficio,

    4 AVG(sal) over (partition by deptno) media_deptno

    5 from scott.emp) e

    6 where sal>media_oficio

    7 or sal>media_deptno;

    ENAME SAL DEPTNO MEDIA_OFICIO MEDIA_DEPTNO

    ---------- ---------- ---------- ------------ ------------

    SCOTT 3000 20 3000 2175

    FORD 3000 20 3000 2175

    MILLER 1300 10 1037,5 2916,66667

    ADAMS 1100 20 1037,5 2175

    BLAKE 2850 30 2758,33333 1566,66667

    JONES 2975 20 2758,33333 2175

    KING 5000 10 5000 2916,66667

    TURNER 1500 30 1400 1566,66667

    ALLEN 1600 30 1400 1566,66667

    9 filas seleccionadas.

    ------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)|

    ------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 14 | 826 | 5 (40)|

    |* 1 | VIEW | | 14 | 826 | 5 (40)|

    | 2 | WINDOW SORT | | 14 | 294 | 5 (40)|

    | 3 | WINDOW SORT | | 14 | 294 | 5 (40)|

    | 4 | TABLE ACCESS FULL| EMP | 14 | 294 | 3 (0)|

    ------------------------------------------------------------------

  • Mejora de SQL: casos y usos

    http://www.optimizacionsqlenoracle.com 303

    Las operaciones 2 y 3 WINDOW SORT corresponden a la clasificacin de los salarios. En uno de ellos por departamento, y por oficio en el otro. La operacin 1 VIEW aplica el filtro de las dos condiciones entre la columna SAL y las pseudocolumnas con el valor calculado de media de salarios. El coste sigue siendo considerablemente menor, y el nmero de accesos a la tabla EMP sigue siendo solo uno.

    Adems de proporcionar una potente capacidad de clculo estadstico, las funciones analticas permiten relacionar informacin por cada una de las filas con respecto a la siguiente o a la anterior en funcin de un criterio. As, las funciones LAG y LEAD proporcionan, por ejemplo, el valor anterior y el siguiente de la ordenacin definida.

    De otro modo, cmo podran consultarse valores de las filas inmediatamente siguientes o anteriores? Quizs parezca que este tipo de consultas no son importantes, pero imagine el siguiente ejemplo: nos piden conocer la fecha del ltimo recibo impagado y la fecha de los ltimos tres impagados anteriores, pues no es lo mismo que un cliente tenga un impago hace un mes y los anteriores fueran impagos de hace dos aos, o que el cliente haya dejado de pagar los ltimos tres recibos.

    El siguiente ejemplo de LAG y LEAD muestra los empleados con el nombre y sueldo de quien cobra menos y el nombre y sueldo de quien cobra ms.

    SQL> select lag(ename,1) over (order by sal desc) COBRA_MAS,

    2 lag(sal,1) over (order by sal desc) SAL_MAS,

    3 ename EMPLEADO,

    4 sal SALARIO,

    5 lead(ename,1) over (order by sal desc) COBRA_MENOS,

    6 lead(sal,1) over (order by sal desc) SAL_MENOS

    7 from scott.emp;

    COBRA_MAS SAL_MAS EMPLEADO SALARIO COBRA_MENO SAL_MENOS

    ---------- ---------- ---------- ---------- ---------- ----------

    KING 5000 FORD 3000

    KING 5000 FORD 3000 SCOTT 3000

    FORD 3000 SCOTT 3000 JONES 2975

    SCOTT 3000 JONES 2975 BLAKE 2850

    JONES 2975 BLAKE 2850 CLARK 2450

    BLAKE 2850 CLARK 2450 ALLEN 1600

    CLARK 2450 ALLEN 1600 TURNER 1500

    ALLEN 1600 TURNER 1500 MILLER 1300

    TURNER 1500 MILLER 1300 WARD 1250

    MILLER 1300 WARD 1250 MARTIN 1250

    WARD 1250 MARTIN 1250 ADAMS 1100

    MARTIN 1250 ADAMS 1100 JAMES 950

    ADAMS 1100 JAMES 950 SMITH 800

    JAMES 950 SMITH 800

    14 filas seleccionadas.

  • Optimizacin SQL en Oracle

    304 http://www.optimizacionsqlenoracle.com

    ----------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)|

    ----------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 14 | 140 | 4 (25)|

    | 1 | WINDOW SORT | | 14 | 140 | 4 (25)|

    | 2 | TABLE ACCESS FULL| EMP | 14 | 140 | 3 (0)|

    ----------------------------------------------------------------

    Este tipo de consultas expresadas sin funciones analticas resultan difciles de comprender y de mantener, adems de implicar accesos una y otra vez a las mismas tablas, o hacer maniobras con ROWNUM para controlar el nmero de filas devueltas, como muestra el siguiente ejemplo: SQL> select emp1.ename, emp1.sal,

    2 emp2.ename, emp2.sal,

    3 emp3.ename, emp3.sal

    4 from (select emp1.*, rownum fila from (select ename, sal from scott.emp order by sal) emp1) emp1,

    5 (select emp2.*, rownum fila from (select ename, sal from scott.emp order by sal) emp2) emp2,

    6 (select emp3.*, rownum fila from (select ename, sal from scott.emp order by sal) emp3) emp3

    7 where emp1.fila(+)=emp2.fila+1

    8 and emp2.fila=emp3.fila+1

    9 order by emp2.sal desc;

    ENAME SAL ENAME SAL ENAME SAL

    ---------- ------- ---------- ------- ---------- -------

    KING 5000 FORD 3000

    KING 5000 FORD 3000 SCOTT 3000

    FORD 3000 SCOTT 3000 JONES 2975

    SCOTT 3000 JONES 2975 BLAKE 2850

    JONES 2975 BLAKE 2850 CLARK 2450

    BLAKE 2850 CLARK 2450 ALLEN 1600

    CLARK 2450 ALLEN 1600 TURNER 1500

    ALLEN 1600 TURNER 1500 MILLER 1300

    TURNER 1500 MILLER 1300 MARTIN 1250

    MILLER 1300 MARTIN 1250 WARD 1250

    MARTIN 1250 WARD 1250 ADAMS 1100

    WARD 1250 ADAMS 1100 JAMES 950

    ADAMS 1100 JAMES 950 SMITH 800

    13 filas seleccionadas.

    ---------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)|

    ---------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 2 | 198 | 13 (31)|

    |* 1 | HASH JOIN OUTER | | 2 | 198 | 13 (31)|

    |* 2 | HASH JOIN | | 2 | 132 | 9 (34)|

    | 3 | VIEW | | 14 | 462 | 4 (25)|

    | 4 | COUNT | | | | |

    | 5 | VIEW | | 14 | 280 | 4 (25)|

    | 6 | SORT ORDER BY | | 14 | 140 | 4 (25)|

    | 7 | TABLE ACCESS FULL| EMP | 14 | 140 | 3 (0)|

    | 8 | VIEW | | 14 | 462 | 4 (25)|

    | 9 | COUNT | | | | |

    | 10 | VIEW | | 14 | 280 | 4 (25)|

    | 11 | SORT ORDER BY | | 14 | 140 | 4 (25)|

    | 12 | TABLE ACCESS FULL| EMP | 14 | 140 | 3 (0)|

    | 13 | VIEW | | 14 | 462 | 4 (25)|

    | 14 | COUNT | | | | |

    | 15 | VIEW | | 14 | 280 | 4 (25)|

    | 16 | SORT ORDER BY | | 14 | 140 | 4 (25)|

    | 17 | TABLE ACCESS FULL | EMP | 14 | 140 | 3 (0)|

    ---------------------------------------------------------------------

  • Mejora de SQL: casos y usos

    http://www.optimizacionsqlenoracle.com 305

    El plan de ejecucin es tres veces ms enrevesado y redundante. Se producen tres accesos a la tabla EMP cuando solo sera necesario uno. Si en lugar de consultar el anterior y el siguiente se quisiesen obtener los dos anteriores y los dos posteriores, la consulta realizada sin funciones analticas debera acceder cinco veces a la tabla EMP para mostrar los mismos resultados, y el cdigo tendra un volumen de texto considerable. Adems, el segundo cdigo resulta ms costoso en su mantenimiento. Qu sucedera si se pretendiese obtener el anterior y el siguiente empleado por salario correspondiente a su departamento? Pueden hacer el ejercicio basndose en la anterior consulta. Por mi parte, sugiero este: SQL> select deptno,

    2 lag(ename,1) over (partition by deptno order by sal desc) COBRA_MAS,

    3 lag(sal,1) over (partition by deptno order by sal desc) SAL_MAS,

    4 ename EMPLEADO,

    5 sal SALARIO,

    6 lead(ename,1) over (partition by deptno order by sal desc) COBRA_MENOS,

    7 lead(sal,1) over (partition by deptno order by sal desc) SAL_MENOS

    8 from scott.emp;

    DEPTNO COBRA_MAS SAL_MAS EMPLEADO SALARIO COBRA_MENO SAL_MENOS

    ---------- ---------- ---------- ---------- ---------- ---------- ----------

    10 KING 5000 CLARK 2450

    10 KING 5000 CLARK 2450 MILLER 1300

    10 CLARK 2450 MILLER 1300

    20 SCOTT 3000 FORD 3000

    20 SCOTT 3000 FORD 3000 JONES 2975

    20 FORD 3000 JONES 2975 ADAMS 1100

    20 JONES 2975 ADAMS 1100 SMITH 800

    20 ADAMS 1100 SMITH 800

    30 BLAKE 2850 ALLEN 1600

    30 BLAKE 2850 ALLEN 1600 TURNER 1500

    30 ALLEN 1600 TURNER 1500 MARTIN 1250

    30 TURNER 1500 MARTIN 1250 WARD 1250

    30 MARTIN 1250 WARD 1250 JAMES 950

    30 WARD 1250 JAMES 950

    14 filas seleccionadas.

    Aqu, en el plan de ejecucin, sigue accedindose una nica vez a la tabla EMP, y solo ha sido necesario aadir la clusula PARTITION BY DEPTNO a las funciones analticas del cdigo. ---------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

    ---------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 12 | 396 | 4 (25)| 00:00:01 |

    | 1 | WINDOW SORT | | 12 | 396 | 4 (25)| 00:00:01 |

    | 2 | TABLE ACCESS FULL| EMP | 12 | 396 | 3 (0)| 00:00:01 |

    ---------------------------------------------------------------------------

  • Mejora de SQL: casos y usos

    http://www.optimizacionsqlenoracle.com 323

    Uso correcto de los tipos de datos Los tipos de datos numricos fueron concebidos para almacenar nmeros. Los tipos de datos de fechas para almacenar fechas. Los tipos de datos alfanumricos para almacenar texto y cifras sin valor numrico en s mismos.

    Repita las frases anteriores varias veces, comprndalas y asmalas. Son ciertas. Cuando los programadores del ncleo de un sistema gestor de base de datos implementaron los tipos de datos siguieron esas premisas para crear estructuras eficientes de almacenamiento de informacin.

    Oracle almacena los nmeros en formato exponencial. Los valores para el tipo NUMBER, el ms estndar, van de -9,99...9x10^125 a 9,99...9x10^125 con 38 dgitos significativos. Utiliza 1 byte para el exponente y 20 bytes para la mantisa, ms un byte en caso de ser un nmero negativo.

    Por ejemplo, para almacenar el valor 555 Oracle utiliza la frmula: 5,55x10^2. Un byte para el exponente, 2 bytes para los tres dgitos significativos de la mantisa (en este caso, para almacenar 555 se necesitan dos bytes).

    Esta es la forma ptima de almacenar valores numricos, cifras, importes. Es la estructura que el gestor de base de datos mejor maneja para calcular, evaluar, convertir, etc.

  • Optimizacin SQL en Oracle

    324 http://www.optimizacionsqlenoracle.com

    Si se usa un tipo de dato VARCHAR2 para almacenar '555', Oracle almacenar tres bytes: uno por cada carcter '5'. No hay ningn significado numrico, no admite sumas o restas, es igual que almacenar 'aaa', con tres bytes para el carcter 'a'. Se tratarn las cifras '5' como texto.

    Las fechas, almacenadas con DATE, por ejemplo, contienen ao, incluyendo la era (antes de Cristo y despus de Cristo), mes y da, con informacin de hora, minuto y segundo a partir de la medianoche.

    Supongamos la misma tabla de VUELOS usada anteriormente, con una columna con la fecha de vuelo en formato texto. SQL> alter table vuelos

    2 add fecha_vuelo_char varchar2(500);

    Tabla modificada.

    SQL> update vuelos

    2 set fecha_vuelo_char=to_char(fecha_vuelo,'YYYYMMDDHH24MISS');

    57711 filas actualizadas.

    SQL> commit;

    Confirmacin terminada.

    Ahora existen dos columnas con la fecha. Una expresada en tipo de dato DATE y la nueva de tipo VARCHAR2. Cada una de estas columnas est indexada y la tabla tiene estadsticas generadas con DBMS_STATS.

    SQL> create index idx_fecha_vuelo

    2 on vuelos(fecha_vuelo);

    ndice creado.

    SQL> create index idx_fecha_vuelo_char

    2 on vuelos(fecha_vuelo_char);

    ndice creado.

    SQL> exec dbms_stats.gather_table_stats(tabname=>'VUELOS',ownname=>'VUELOS')

    Procedimiento PL/SQL terminado correctamente.

    En estos trminos, en la bsqueda de vuelos para una hora en concreto, el optimizador estima de forma distinta la cardinalidad de elementos filtrando por una hora que la producida por la distancia en bytes de una cadena de caracteres a otra.

  • Mejora de SQL: casos y usos

    http://www.optimizacionsqlenoracle.com 325

    Estimacin de filas consultando valores entre fechas expresadas en formato carcter

    SQL> explain plan for

    2 select *

    3 from vuelos

    4 where fecha_vuelo_char between '2004060123'

    5 and '2004060200';

    Explicado.

    SQL> @?/rdbms/admin/utlxpls

    PLAN_TABLE_OUTPUT

    ----------------------------------------------------------------------------

    Plan hash value: 672684626

    ----------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

    ----------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 223 | 13380 | 172 (2)| 00:00:03 |

    |* 1 | TABLE ACCESS FULL| VUELOS | 223 | 13380 | 172 (2)| 00:00:03 |

    ----------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    1 - filter("FECHA_VUELO_CHAR"='2004060123')

    En este caso, el nmero de filas para los vuelos con fecha 1-junio-2004 entre las 23:00 y la medianoche se estima en 223, suficientes para justificar un FULL SCAN de la tabla VUELOS con un coste de 172. La misma consulta sobre la columna de fecha de vuelo en formato fecha, y no en formato carcter, muestra un plan de ejecucin distinto. El optimizador prev acceder a un total de 102 filas y estima ms conveniente acceder a la tabla mediante el ndice, resultando un coste de 104. Estimacin de filas consultando valores entre fechas expresadas en formato fecha

    SQL> explain plan for

    2 select *

    3 from vuelos

    4 where fecha_vuelo between to_date('2004060123','YYYYMMDDHH24')

    5 and to_date('2004060200','YYYYMMDDHH24');

    Explicado.

  • Optimizacin SQL en Oracle

    326 http://www.optimizacionsqlenoracle.com

    SQL> @?/rdbms/admin/utlxpls

    PLAN_TABLE_OUTPUT -----------------------------------------------------------------------------------------------

    Plan hash value: 3929702166

    -----------------------------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

    -----------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 102 | 6120 | 104 (0)| 00:00:02 |

    | 1 | TABLE ACCESS BY INDEX ROWID| VUELOS | 102 | 6120 | 104 (0)| 00:00:02 |

    |* 2 | INDEX RANGE SCAN | IDX_FECHA_VUELO | 102 | | 2 (0)| 00:00:01 |

    -----------------------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - access("FECHA_VUELO">=TO_DATE(' 2004-06-01 23:00:00', 'syyyy-mm-dd hh24:mi:ss')

    AND "FECHA_VUELO" select count(*)

    2 from vuelos

    3 where fecha_vuelo_char between '2004060123'

    4 and '2004060200';

    COUNT(*)

    ----------

    89

    SQL> select count(*)

    2 from vuelos

    3 where fecha_vuelo between to_date('2004060123','YYYYMMDDHH24')

    4 and to_date('2004060200','YYYYMMDDHH24');

    COUNT(*)

    ----------

    89

    Aunque ninguna de las dos estimaciones haya acertado por completo, la estimacin de fechas ha sido la ms acertada en las mltiples variaciones de consulta sobre rangos de texto para obtener la estimacin de filas en una hora concreta. Los valores estimados pueden llegar a variar considerablemente. La misma sentencia, expresada entre las 23:00 y las 23:59, retorna el mismo nmero de filas. La estimacin de vuelos filtrando sobre la columna en formato carcter llega a determinar nicamente dos filas, mientras que cuando compara las fechas afina mucho ms el clculo, estimando los mismos 102 elementos que antes.

  • Mejora de SQL: casos y usos

    http://www.optimizacionsqlenoracle.com 327

    Estimacin de filas consultando valores entre fechas expresadas en formato carcter

    SQL> explain plan for

    2 select *

    3 from vuelos

    4 where fecha_vuelo_char between '200406012300'

    5 and '20040601235959';

    Explicado.

    SQL> @?/rdbms/admin/utlxpls

    PLAN_TABLE_OUTPUT

    -------------------------------------------------------------------------------------

    Plan hash value: 783782159

    ----------------------------------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

    ----------------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 2 | 120 | 4 (0)| 00:00:01 |

    | 1 | TABLE ACCESS BY INDEX ROWID| VUELOS | 2 | 120 | 4 (0)| 00:00:01 |

    |* 2 | INDEX RANGE SCAN | IDX_FECHA_VUELO_CHAR | 2 | | 2 (0)| 00:00:01 |

    ----------------------------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - access("FECHA_VUELO_CHAR">='200406012300' AND

    "FECHA_VUELO_CHAR" select count(*)

    2 from vuelos

    3 where fecha_vuelo_char between '200406012300'

    4 and '20040601235959';

    COUNT(*)

    ----------

    89

    Estimacin de filas consultando valores entre fechas expresadas en formato fecha

    SQL> explain plan for

    2 select *

    3 from vuelos

    4 where fecha_vuelo between to_date('2004060123','YYYYMMDDHH24')

    5 and to_date('20040601235959','YYYYMMDDHH24MISS');

    Explicado.

  • Optimizacin SQL en Oracle

    328 http://www.optimizacionsqlenoracle.com

    SQL> @?/rdbms/admin/utlxpls

    PLAN_TABLE_OUTPUT

    ----------------------------------------------------------------------------------

    Plan hash value: 3929702166

    -----------------------------------------------------------------------------------------------

    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

    -----------------------------------------------------------------------------------------------

    | 0 | SELECT STATEMENT | | 102 | 6120 | 104 (0)| 00:00:02 |

    | 1 | TABLE ACCESS BY INDEX ROWID| VUELOS | 102 | 6120 | 104 (0)| 00:00:02 |

    |* 2 | INDEX RANGE SCAN | IDX_FECHA_VUELO | 102 | | 2 (0)| 00:00:01 |

    -----------------------------------------------------------------------------------------------

    Predicate Information (identified by operation id):

    ---------------------------------------------------

    2 - access("FECHA_VUELO">=TO_DATE(' 2004-06-01 23:00:00', 'syyyy-mm-dd hh24:mi:ss')

    AND "FECHA_VUELO" select count(*)

    2 from vuelos

    3 where fecha_vuelo between to_date('2004060123','YYYYMMDDHH24')

    4 and to_date('20040601235959','YYYYMMDDHH24MISS');

    COUNT(*)

    ----------

    89

    En resumen, cuando las fechas son almacenadas como fechas, las estimaciones de la cardinalidad de elementos es mucho ms acertada que cuando el optimizador tiene que estimar cuntas filas distintas puede haber entre dos cadenas de caracteres que enmascaren una fecha. El optimizador, segn los caracteres usados para expresar un intervalo de una hora entre dos fechas expresadas en carcter, ha llegado a estimar desde 2 filas a 223. Esto puede generar planes de ejecucin diferentes escribiendo la mscara de fecha en una u otra forma, y ninguno de ellos podra traducir literalmente que lo que se precisa es el nmero de filas existente entre dos fechas concretas. La norma es sencilla. Un nmero ha de almacenarse en un tipo de datos numrico (NUMBER, FLOAT, INTEGER, etc.). Un texto, aunque incluya nmeros, en formato alfanumrico (CHAR, VARCHAR2, etc.). Una fecha ha de almacenarse en un tipo de datos de fecha (DATE, TIMESTAMP, etc.). El optimizador supone que la informacin contenida es de ese tipo, y la evala como si se tratase de nmeros, texto o fechas. No hay motivo para almacenar fechas en formato numrico o carcter. S lo hay para usar los tipos de datos adecuados: el optimizador considerar los datos como del tipo al que pertenecen. Mejora la eficiencia de la compilacin y la estimacin de valores en filtros por rangos.