2012-02-03 35 views
7

Para consultar top-n filas en Oracle, es general utilizar ROWNUM. Así que la siguiente consulta parece bien (consigue más recientes 5 pagos):Oracle ROWNUM rendimiento

select a.paydate, a.amount 
from (
    select t.paydate, t.amount 
    from payments t 
    where t.some_id = id 
    order by t.paydate desc 
) a 
where rownum <= 5; 

Pero por muy grandes mesas, que no es eficaz - para mí correr el agua durante ~ 10 minutos. así que he intentado otras consultas, y que terminó con éste, que tiene una duración de menos de un segundo:

select * 
from (
    select a.*, rownum 
    from (select t.paydate, t.amount 
     from payments t 
     where t.some_id = id 
     order by t.paydate desc) a 
) 
where rownum <= 5; 

para averiguar lo que está sucediendo, miré planes de ejecución para cada consulta. Por primera consulta:

SELECT STATEMENT, GOAL = ALL_ROWS 7 5 175 
COUNT STOPKEY   
VIEW 7 5 175 
TABLE ACCESS BY INDEX ROWID 7 316576866 6331537320 
INDEX FULL SCAN DESCENDING 4 6 

Y por segundo:

SELECT STATEMENT, GOAL = ALL_ROWS 86 5 175 
COUNT STOPKEY   
VIEW 86 81 2835 
COUNT   
VIEW 86 81 1782 
SORT ORDER BY 86 81 1620 
TABLE ACCESS BY INDEX ROWID 85 81 1620 
INDEX RANGE SCAN 4 81 

Obviamente, es ÍNDICE SCAN completos que descienden que hace primera consulta ineficiente para grandes mesas. Pero realmente no puedo diferenciar la lógica de dos consultas mirándolas. ¿Alguien podría explicarme las diferencias lógicas entre dos consultas en lenguaje humano?

¡Gracias de antemano!

+2

id es una variable de enlace, no (debería ser: id?) Si es así, ¿qué valor se usa (¿lo mismo?) – tbone

+2

No creo que el 'rownum' que está utilizando para el filtro en la segunda versión sea se garantiza que será el mismo que en el primero; ¿Cree que necesita alias su segunda consulta y referenciarla, o agregar 'order by rownum' en la consulta en contra de' a'? Sin embargo, dudo que esto esté afectando la velocidad. –

Respuesta

3

Antes que nada, como mencioné en el comentario de Alex, no estoy seguro de que su segunda versión esté 100% garantizada para darle las filas correctas, ya que el bloque "medio" de la consulta no tiene un order by explícito , Oracle no tiene la obligación de pasar las filas hasta el bloque de consulta externa en ningún orden específico. Sin embargo, no parece haber ninguna razón particular para cambiar el orden en que se pasan las filas desde el bloque más interno, por lo que en la práctica probablemente funcione.

Y esta es la razón por la que Oracle elige un plan diferente para la segunda consulta: lógicamente no puede aplicar la operación STOPKEY al bloque de consulta más interno.

Creo que en el primer caso, el optimizador está asumiendo que los valores de id están bien distribuidos y, para cualquier valor dado, es probable que haya algunas transacciones muy recientes. Como puede ver que solo necesita encontrar las 5 coincidencias más recientes, calcula que parece ser más eficiente escanear las filas en orden descendente de paydate usando un índice, buscar el id. Correspondiente y otros datos de la tabla, y parar cuando se encuentran las primeras 5 coincidencias. Sospecho que verás un rendimiento muy diferente para esta consulta, dependiendo del valor de identificación específico que uses: si el ID tiene mucha actividad reciente, las filas se encontrarán muy rápidamente, pero si no lo hace, el escaneo del índice puede tener que hacer mucho más trabajo.

En el segundo caso, creo que no es posible aplicar la optimización STOPKEY al bloque más interno debido a la capa adicional de anidación. En ese caso, el escaneo completo del índice sería mucho menos atractivo, ya que siempre necesitaría escanear todo el índice. Por lo tanto, elige hacer una búsqueda de índice en id (supongo) seguido de un tipo real en la fecha. Si el valor dado de id coincide con un pequeño subconjunto de filas, es probable que sea más eficiente, pero si proporciona un id que tiene muchas filas repartidas por toda la tabla, esperaría que fuera más lento, ya que tendrá para acceder y clasificar muchas filas.

Por lo tanto, supongo que sus pruebas han utilizado id valor (es) que tienen relativamente pocas filas que no son muy recientes.Si este es un caso de uso típico, entonces la segunda consulta es probablemente mejor para usted (una vez más, con la advertencia de que no estoy seguro si está técnicamente garantizado para producir el conjunto de resultados correcto). Pero si es más probable que los valores típicos tengan muchas filas coincidentes y/o más probabilidades de tener 5 filas muy recientes, entonces la primera consulta y el plan podrían ser mejores.

+0

¡Gran explicación! Gracias. @Alex: parece que es mejor agregar 'order by rownum' ya que agrega" SORT ORDER BY STOPRKEY "en el plan exec, mientras que aliasing' rownum' elimina "COUNT STOPKEY" en el plan de ejecución. Pero, como notaste, no he visto cambios en la velocidad. – Bazi

Cuestiones relacionadas