2009-05-05 20 views
11

Tengo una consulta SQL que se ve algo como esto:¿Cómo puedo acelerar row_number en Oracle?

SELECT * FROM(
    SELECT 
     ..., 
     row_number() OVER(ORDER BY ID) rn 
    FROM 
     ... 
) WHERE rn between :start and :end 

Esencialmente, es el ORDER BY parte que es frenar las cosas. Si tuviera que eliminarlo, el costo EXPLAIN disminuye en un orden de magnitud (más de 1000x). Intenté esto:

SELECT 
    ... 
FROM 
    ... 
WHERE 
    rownum between :start and :end 

Pero esto no da los resultados correctos. ¿Hay alguna manera fácil de acelerar esto? ¿O tendré que pasar más tiempo con la herramienta EXPLAIN?

Respuesta

12

ROW_NUMBER es bastante ineficiente en Oracle.

Ver el artículo en mi blog para los detalles de rendimiento:

Para su consulta específica, me gustaría recomendar que reemplazarlo con ROWNUM y asegurarse de que se utiliza el índice :

SELECT * 
FROM (
     SELECT /*+ INDEX_ASC(t index_on_column) NOPARALLEL_INDEX(t index_on_column) */ 
       t.*, ROWNUM AS rn 
     FROM table t 
     ORDER BY 
       column 
     ) 
WHERE rn >= :start 
     AND rownum <= :end - :start + 1 

Esta consulta utilizará COUNT STOPKEY

También, o bien asegúrese de que column no sea nulo, o agregue la condición WHERE column IS NOT NULL.

De lo contrario, el índice no se puede utilizar para recuperar todos los valores.

Tenga en cuenta que no puede usar ROWNUM BETWEEN :start and :end sin una subconsulta.

ROWNUM siempre se asigna al último y se marca el último, así es ROWNUM siempre vienen en orden sin espacios.

Si utiliza ROWNUM BETWEEN 10 and 20, la primera fila que satisifies todas las demás condiciones se convertirá en un candidato para el retorno, asignado temporalmente con ROWNUM = 1 y no pasan la prueba de ROWNUM BETWEEN 10 AND 20.

Luego, la siguiente fila será candidata, se le asignará ROWNUM = 1 y se producirá un error, etc., por lo que, finalmente, no se devolverán las filas.

Esto debería solucionarse poniendo ROWNUM en la subconsulta.

+0

Funciona como una Sin embargo, las sugerencias del optimizador no parecían hacer una diferencia apreciable. –

+3

Eso significa que "CBO" fue lo suficientemente inteligente como para recoger los índices. En realidad, era ROWNUM en lugar de ROW_NUMBER lo que importaba aquí. – Quassnoi

+0

Pero aún dejaría las pistas o crearía un BOSQUEJO, en caso de que la CBO cambiara de opinión :) – Quassnoi

1

¿Está su columna ORDER BY indexada? Si no, ese es un buen lugar para comenzar.

+0

En realidad, no fue así. Pero cambiarlo a una fila que está indexada no ayuda. Gracias por hacer la sugerencia obvia sin embargo. :-) –

+1

Un índice solo ayudaría a mejorar ORDER BY si la ruta de acceso podría usar ese índice (es decir, si buscaba un rango de ID). –

0

Pase más tiempo con la herramienta EXPLAIN PLAN. Si ve una TABLA DE ESCANEAR, debe cambiar su consulta.

Su consulta tiene poco sentido para mí. Consultar sobre un ROWID parece pedir problemas. No hay información relacional en esa consulta. ¿Es la consulta real con la que tiene problemas o un ejemplo que inventó para ilustrar su problema?

+0

Es paginación. Y eso es esencialmente lo que hace la consulta al menos con la paginación. Acabo de sacar el resto de la consulta (principalmente porque no es trivial). Todas las elipsis son donde he cortado cosas por brevedad. –

4

Parece una consulta de paginación para mí.

De este artículo ASKTOM (alrededor del 90% abajo de la página):

You need to order by something unique for these pagination queries, so that ROW_NUMBER is assigned deterministically to the rows each and every time.

también sus consultas son no está cerca de la misma así que no estoy seguro de lo que el beneficio de la comparación de los costos de una para el otro es.

+1

En realidad, ese artículo me ayudó a escribir la consulta. Sin embargo, no me di cuenta de la parte sobre ordenar por identificadores únicos. También hay una sugerencia del optimizador de consultas que me perdí. Lo probaré en el trabajo mañana! –

+0

;) pensó que parecía familiar. first_rows puede ser increíble con las consultas de paginación. – David

+0

¡Eso y el consejo de Quassnoi hicieron que mi consulta fuera casi constante! Desearía poder seleccionar dos respuestas. :-( –

1

Parte del problema es qué tan grande es el lapso de 'inicio' a 'final' y dónde 'viven'. Digamos que tiene un millón de filas en la tabla, y quiere las filas 567,890 a 567,900, entonces tendrá que vivir con el hecho de que tendrá que pasar por toda la tabla, ordenar todo eso por ID , y averigüe qué filas caen en ese rango.

En resumen, eso es mucho trabajo, por lo que el optimizador le da un alto costo.

Tampoco es algo que un índice pueda ayudar mucho. Un índice daría la orden, pero en el mejor de los casos, eso le da un lugar para comenzar y luego continúa leyendo hasta llegar a la entrada 567.900.

Si le muestra a su usuario final 10 elementos a la vez, puede valer la pena realmente agarrar los 100 mejores de la base de datos, y luego tener la aplicación dividir esos 100 en diez pedazos.

+0

Esto suena apropiado. Realmente estoy sacando cerca de 15,000 registros de ~ 2 millones de registros. Estamos limitados en la cantidad de tiempo que puede llevar una consulta, y extraer todos los 15k registros a la vez estaba causando tiempos de espera. , Pensé que revisar los resultados evitaría esto. Supongo que esto solo significa que tendré que pasar por la pesadilla burocrática de solicitar un tiempo de espera más largo. –

+0

¡Espero que no envíe 15,000 filas al usuario! –

Cuestiones relacionadas