¿Cómo seleccionar los primeros registros 'N' de una base de datos que contiene millones de registros?

StackOverflow https://stackoverflow.com/questions/1410048

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:

  1. ¿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).
  2. Cualquier forma mejor en SQL para mejorar la consulta para ordenar solo N elementos y regresan rápidamente tiempo.
¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top