¿Cómo seleccionar los primeros registros 'N' de una base de datos que contiene millones de registros?
-
05-07-2019 - |
Pregunta
Tengo una base de datos Oracle llena de millones de registros. Estoy tratando de escribir una consulta SQL que devuelve el primer 'N & Quot; registros ordenados (digamos 100 registros) de la base de datos según ciertas condiciones.
SELECT *
FROM myTable
Where SIZE > 2000
ORDER BY NAME DESC
Luego seleccione programáticamente los primeros N registros.
El problema con este enfoque es:
- La consulta da como resultado medio millón registros y " ORDEN POR NOMBRE " causas todos los registros que se ordenarán en NAME en orden descendente. Esta clasificación está tomando mucho tiempo. (casi 30-40 segundos. Si omito ORDER BY, solo lleva 1 segundo).
- Después del tipo que me interesa solo los primeros N (100) registros. Por lo tanto, la clasificación de los registros completos no es útil.
Mis preguntas son:
- ¿Es posible especificar la 'N' en consulta en sí? (por lo que ese orden se aplica solo a N registros y la consulta se vuelve más rápida).
- Cualquier forma mejor en SQL para mejorar la consulta para ordenar solo N elementos y regresan rápidamente tiempo.
Solución
Si su propósito es encontrar 100 filas aleatorias y ordenarlas después, entonces La solución de Lasse es correcta. Si, como creo, desea ordenar las primeras 100 filas por nombre mientras descarta las otras, generaría una consulta como esta:
SELECT *
FROM (SELECT *
FROM myTable
WHERE SIZE > 2000 ORDER BY NAME DESC)
WHERE ROWNUM <= 100
El optimizador comprenderá que es una consulta TOP-N y podrá usar un índice en NAME. No tendrá que ordenar todo el conjunto de resultados, solo comenzará al final del índice y lo leerá al revés y se detendrá después de 100 filas.
También puede agregar una pista a su consulta original para que el optimizador entienda que solo le interesan las primeras filas. Esto probablemente generará una ruta de acceso similar:
SELECT /*+ FIRST_ROWS*/* FROM myTable WHERE SIZE > 2000 ORDER BY NAME DESC
Editar: simplemente agregar AND rownum <= 100
a la consulta no funcionará ya que en Oracle rownum se atribuye antes de ordenar: es por eso que debe usar una subconsulta. Sin la subconsulta, Oracle seleccionará 100 filas aleatorias y luego las ordenará.
Otros consejos
Esto muestra cómo elegir las N filas superiores según su versión de Oracle.
De Oracle 9i en adelante, el RANGO () y Las funciones DENSE_RANK () se pueden usar para determinar las filas N superiores. Ejemplos:
Obtenga los 10 mejores empleados según su salario
SELECCIONE esmalte, sal DESDE (SELECCIONAR esmalte, sal, RANGO () SOBRE (PEDIDO POR sal DESC) sal_rank DESDE emp) DONDE sal_rank & Lt; = 10;
Seleccione los empleados que están entre los 10 mejores salarios
SELECCIONE esmalte, sal DESDE (SELECCIONAR esmalte, sal, DENSE_RANK () OVER (ORDEN POR sal DESC) sal_dense_rank DESDE emp) DONDE sal_dense_rank & Lt; = 10;
La diferencia entre los dos se explica aquí
Agregue esto:
AND rownum <= 100
a su cláusula WHERE.
Sin embargo, esto no hará lo que estás pidiendo.
Si desea elegir 100 filas aleatorias, ordenarlas y luego devolverlas, primero deberá formular una consulta sin ORDER BY, luego limitar eso a 100 filas, luego seleccionar de eso y ordenar.
Esto podría funcionar, pero desafortunadamente no tengo un servidor Oracle disponible para probar:
SELECT *
FROM (
SELECT *
FROM myTable
WHERE SIZE > 2000
AND rownum <= 100
) x
ORDER BY NAME DESC
Pero observe el " random " parte allí, estás diciendo " dame 100 filas con SIZE > 2000, no me importa cuáles 100 & Quot ;.
¿Es eso realmente lo que quieres?
Y no, en realidad no obtendrá un resultado aleatorio, en el sentido de que cambiará cada vez que consulte el servidor, pero estará a merced del optimizador de consultas. Si la carga de datos y las estadísticas de índice para esa tabla cambian con el tiempo, en algún momento puede obtener datos diferentes a los de la consulta anterior.
Su problema es que el ordenamiento se realiza cada vez que se ejecuta la consulta. Puede eliminar la operación de clasificación utilizando un índice (el optimizador puede utilizar un índice para eliminar una operación de clasificación) si la columna ordenada se declara NO NULA.
(Si la columna es anulable, todavía es posible, ya sea (a) agregando un predicado NOT NULL a la consulta, o (b) agregando un índice basado en funciones y modificando la cláusula ORDER BY en consecuencia).
Solo como referencia, en Oracle 12c, esta tarea se puede hacer usando la cláusula FETCH
. Puede ver aquí para ejemplos y enlaces de referencia adicionales con respecto a este asunto.