¿Cómo limito el número de filas devueltas por una consulta de Oracle después de realizar el pedido?

¿Hay alguna manera de hacer un Oracle la consulta se comporta como si contuviera un MySQL limit ¿cláusula?

En MySQL, puedo hacer esto:

select * 
from sometable
order by name
limit 20,10

para obtener las filas 21 a 30 (omita las primeras 20, dé las siguientes 10). Las filas se seleccionan después de order by, por lo que realmente comienza con el nombre número 20 en orden alfabético.

En Oracle, lo único que la gente menciona es el rownum pseudocolumna, pero se evalúa antes order by, lo que significa esto:

select * 
from sometable
where rownum <= 10
order by name

devolverá un conjunto aleatorio de diez filas ordenadas por nombre, que normalmente no es lo que quiero. Tampoco permite especificar un desplazamiento.

preguntado el 22 de enero de 09 a las 17:01

Estandarizado en SQL: 2008. -

El límite fue anunciado por Tom Kyte para Oracle 12c ... -

¿Obteniendo la página siguiente en un conjunto de resultados? -

@YaroslavShabalin En particular, una búsqueda paginada utiliza este patrón todos el tiempo. Casi cualquier aplicación con cualquier tipo de función de búsqueda la utilizará. Otro caso de uso sería cargar solo una parte de una lista larga o una tabla del lado del cliente y darle al usuario la opción de expandirse. -

@YaroslavShabalin No puede obtener un conjunto de resultados diferente a menos que los datos subyacentes cambien debido a la ORDER BY. Ese es el objetivo de ordenar primero. Si los datos subyacentes cambian y su conjunto de resultados cambia debido a ello, ¿por qué no mostrar al usuario los resultados actualizados en lugar de información desactualizada? Además, la gestión estatal es una plaga que debe evitarse en la medida de lo posible. Es una fuente constante de complicaciones y errores; es por eso que lo funcional se está volviendo tan popular. ¿Y cuándo sabría que caduca todo el conjunto de resultados en la memoria? En la web, no tienes forma de saber cuándo se va el usuario. -

15 Respuestas

A partir de Oracle 12c R1 (12.1), hay is a cláusula de limitación de fila. No usa familiar LIMIT sintaxis, pero puede hacer mejor el trabajo con más opciones. Puedes encontrar el sintaxis completa aquí. (Lea también más sobre cómo funciona esto internamente en Oracle en esta respuesta).

Para responder a la pregunta original, aquí está la consulta:

SELECT * 
FROM   sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

(Para versiones anteriores de Oracle, consulte otras respuestas en esta pregunta)


Ejemplos:

Los siguientes ejemplos fueron citados de página vinculada, con la esperanza de prevenir la pudrición del enlace.

Preparar

CREATE TABLE rownum_order_test (
  val  NUMBER
);

INSERT ALL
  INTO rownum_order_test
SELECT level
FROM   dual
CONNECT BY level <= 10;

COMMIT;

¿Qué hay en la mesa?

SELECT val
FROM   rownum_order_test
ORDER BY val;

       VAL
----------
         1
         1
         2
         2
         3
         3
         4
         4
         5
         5
         6
         6
         7
         7
         8
         8
         9
         9
        10
        10

20 rows selected.

Conseguir primero N filas

SELECT val
FROM   rownum_order_test
ORDER BY val DESC
FETCH FIRST 5 ROWS ONLY;

       VAL
----------
        10
        10
         9
         9
         8

5 rows selected.

Conseguir primero N filas, si Nth la fila tiene corbatas, obtén todas las filas atadas

SELECT val
FROM   rownum_order_test
ORDER BY val DESC
FETCH FIRST 5 ROWS WITH TIES;

       VAL
----------
        10
        10
         9
         9
         8
         8

6 rows selected.

Notable x% de filas

SELECT val
FROM   rownum_order_test
ORDER BY val
FETCH FIRST 20 PERCENT ROWS ONLY;

       VAL
----------
         1
         1
         2
         2

4 rows selected.

Usando un desplazamiento, muy útil para la paginación.

SELECT val
FROM   rownum_order_test
ORDER BY val
OFFSET 4 ROWS FETCH NEXT 4 ROWS ONLY;

       VAL
----------
         3
         3
         4
         4

4 rows selected.

Puede combinar la compensación con porcentajes

SELECT val
FROM   rownum_order_test
ORDER BY val
OFFSET 4 ROWS FETCH NEXT 20 PERCENT ROWS ONLY;

       VAL
----------
         3
         3
         4
         4

4 rows selected.

Respondido el 20 de junio de 20 a las 10:06

Solo para extender: OFFSET FETCH la sintaxis es un azúcar de sintaxis. Detalles - lukasz szozda

¿Cómo podemos obtener el LÍMITE y la COMPENSACIÓN en Oracle 11G? - Pra_A

@Pra_A No hay soporte nativo en 11G para LIMIT/OFFSET. Si marca las otras respuestas, todas de una forma u otra han implementado realmente el límite y la compensación. - sampathris

Puede usar una subconsulta para esto como

select *
from  
( select * 
  from emp 
  order by sal desc ) 
where ROWNUM <= 5;

Echa también un vistazo al tema. En ROWNUM y resultados limitados en Oracle / AskTom para obtener más información.

Actualizar: Para limitar el resultado con los límites superior e inferior, las cosas se vuelven un poco más infladas con

select * from 
( select a.*, ROWNUM rnum from 
  ( <your_query_goes_here, with order by> ) a 
  where ROWNUM <= :MAX_ROW_TO_FETCH )
where rnum  >= :MIN_ROW_TO_FETCH;

(Copiado del artículo AskTom especificado)

actualización 2: A partir de Oracle 12c (12.1), hay una sintaxis disponible para limitar filas o comenzar en compensaciones.

SELECT * 
FROM   sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

Ver esta respuesta para más ejemplos. Gracias a Krumia por la pista.

Respondido 25 Oct 14, 20:10

Esta es definitivamente la forma de hacerlo, pero tenga en cuenta (como dice el artículo de ask tom) que el rendimiento de la consulta se degrada a medida que aumenta su max rownum. Esta es una buena solución para los resultados de consultas en las que solo desea ver las primeras páginas, pero si está utilizando esto como un mecanismo para que el código recorra una tabla completa, sería mejor refactorizar su código: chris gill

+1 en su versión inferior / superior en realidad me ayudó a solucionar un problema en el que una simple cláusula de rownum de límite superior estaba ralentizando drásticamente mi consulta. - Kelvin

La "solución analítica con una sola consulta anidada" de Leigh Riffel es la única. - Darren Hicks

El artículo de AskTom también tiene una sugerencia de optimizador que usa SELECT / * + FIRST_ROWS (n) / un., rownum rnum La barra oblicua de cierre debe ir precedida de un asterisco. SO lo está limpiando. - david mann

Tenga en cuenta que para Oracle 11, un SELECT externo con ROWNUM evitará que llame a deleteRow en un UpdatableResultSet (con ORA-01446), ¡esperando ese cambio de 12c R1! - nsandersen

Hice algunas pruebas de rendimiento para los siguientes enfoques:

Preguntar

select * from (
  select a.*, ROWNUM rnum from (
    <select statement with order by clause>
  ) a where rownum <= MAX_ROW
) where rnum >= MIN_ROW

Pruebas analíticas

select * from (
  <select statement with order by clause>
) where myrow between MIN_ROW and MAX_ROW

Alternativa corta

select * from (
  select statement, rownum as RN with order by clause
) where a.rn >= MIN_ROW and a.rn <= MAX_ROW

Resultados

La tabla tenía 10 millones de registros, la ordenación estaba en una fila de fecha y hora no indexada:

  • Explique que el plan mostró el mismo valor para las tres selecciones (323168)
  • Pero el ganador es AskTom (con el seguimiento analítico de cerca)

Seleccionar las primeras 10 filas tomó:

  • AskTom: 28-30 segundos
  • Analítico: 33-37 segundos
  • Alternativa corta: 110-140 segundos

Seleccionar filas entre 100,000 y 100,010:

  • AskTom: 60 segundos
  • Analítico: 100 segundos

Seleccionar filas entre 9,000,000 y 9,000,010:

  • AskTom: 130 segundos
  • Analítico: 150 segundos

Respondido el 29 de junio de 18 a las 10:06

Buen trabajo. ¿Probaste la alternativa corta con un entre en lugar de> = y <=? - Mathieu Longtin

@MathieuLongtin BETWEEN es solo una abreviatura de >= AND <= (stackoverflow.com/questions/4809083/entre-cláusula-versus-y) - mimbre

zeldi - ¿En qué versión estaba? Oracle ha realizado mejoras de rendimiento analítico en 11.1. y 11.2. - Leigh Riffel

@Leigh Riffel Era el 10.2.0.5; algún día podría tomarme un tiempo y también comprobar la versión 11i. - zeldi

Hice algunas pruebas rápidas y obtuve resultados similares para 12c. El nuevo offset La sintaxis tiene el mismo plan y rendimiento que el enfoque analítico. - jon heller

Una solución analítica con solo una consulta anidada:

SELECT * FROM
(
   SELECT t.*, Row_Number() OVER (ORDER BY name) MyRow FROM sometable t
) 
WHERE MyRow BETWEEN 10 AND 20;

Rank() podría ser sustituido por Row_Number() pero puede devolver más registros de los que espera si hay valores duplicados para el nombre.

Respondido el 14 de enero de 16 a las 10:01

Amo la analítica. Es posible que desee aclarar cuál sería la diferencia de comportamiento entre Rank () y Row_Number (). - david costa

De hecho, no estoy seguro de por qué no pensé en duplicados. Por lo tanto, en este caso, si hay valores duplicados para el nombre, RANK podría proporcionar más registros de los que espera, por lo que debe usar Row_Number. - Leigh Riffel

Si menciona rank() también vale la pena señalar dense_rank() que puede ser más útil para el control de salida ya que este último no "salta" números, mientras que rank() lata. En cualquier caso para esta pregunta row_number() es el más adecuado. Otra no es que esta técnica sea aplicable a cualquier base de datos que admita las funciones mencionadas. - Usado_por_ya

En Oracle 12c (consulte la cláusula de limitación de filas en Referencia SQL):

SELECT * 
FROM sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

respondido 27 nov., 14:07

Y, por supuesto, tuvieron que usar una sintaxis totalmente diferente a la de todos los demás hasta ahora: Mathieu Longtin

Claramente, después de sentarse con todos los demás proveedores para acordar LIMIT en SQL: 2008, luego tuvieron que tomar una hoja del libro de Microsoft y romper el estándar. - beldaz

Curiosamente, escuché recientemente que el estándar más reciente incluye esta sintaxis, por lo que tal vez Oracle lo introdujo primero antes de implementarlo. Podría decirse que es más flexible que LIMIT ... OFFSET - beldaz

@Derek: Sí, no seguir el estándar es lamentable. Pero la funcionalidad recién introducida en 12cR1 es más poderosa que solo LIMIT n, m (Ver mi respuesta). Por otra parte, Oracle debería haber implementado LIMIT n, m como azúcar sintáctico, ya que es equivalente a OFFSET n ROWS FETCH NEXT m ROWS ONLY. - sampathris

@Derek: En realidad, acabo de notar este comentario en el manual de PostgreSQL postgresql.org/docs/9.0/static/sql-select.html#AEN69535 "Las cláusulas LIMIT y OFFSET son sintaxis específica de PostgreSQL, también utilizada por MySQL. El estándar SQL: 2008 ha introducido las cláusulas OFFSET ... FETCH {FIRST | NEXT} ... para la misma funcionalidad". Entonces, LIMIT nunca fue parte del estándar. - beldaz

Estándar SQL

Desde la versión 12c, Oracle admite SQL: 2008 Standard, que proporciona la siguiente sintaxis para limitar el conjunto de resultados de SQL:

SELECT
    title
FROM
    post
ORDER BY
    id DESC
FETCH FIRST 50 ROWS ONLY

Oracle 11g y versiones anteriores

Antes de la versión 12c, para obtener el Top-N registros, tenías que usar una tabla derivada y el ROWNUM pseudocolumna:

SELECT *
FROM (
    SELECT
        title
    FROM
        post
    ORDER BY
        id DESC
)
WHERE ROWNUM <= 50

Respondido el 10 de enero de 21 a las 06:01

Las consultas de paginación con ordenación son realmente complicadas en Oracle.

Oracle proporciona una pseudocolumna ROWNUM que devuelve un número que indica el orden en el que la base de datos selecciona la fila de una tabla o un conjunto de vistas unidas.

ROWNUM es una pseudocolumna que mete en problemas a mucha gente. Un valor ROWNUM no se asigna permanentemente a una fila (este es un malentendido común). Puede resultar confuso cuando se asigna realmente un valor ROWNUM. Se asigna un valor ROWNUM a una fila después de que pasa los predicados de filtro de la consulta pero antes de la agregación u ordenación de consultas.

Además, un valor ROWNUM se incrementa solo después de que se asigna.

Es por eso que la siguiente consulta no devuelve filas:

 select * 
 from (select *
       from some_table
       order by some_column)
 where ROWNUM <= 4 and ROWNUM > 1; 

La primera fila del resultado de la consulta no pasa el predicado ROWNUM> 1, por lo que ROWNUM no se incrementa a 2. Por esta razón, ningún valor de ROWNUM es mayor que 1, por lo tanto, la consulta no devuelve filas.

La consulta correctamente definida debería verse así:

select *
from (select *, ROWNUM rnum
      from (select *
            from skijump_results
            order by points)
      where ROWNUM <= 4)
where rnum > 1; 

Obtenga más información sobre las consultas de paginación en mis artículos sobre vertabelo blog:

Respondido 12 Abr '14, 18:04

La primera fila del resultado de la consulta no pasa ROWNUM> 1 predicado (…) - voto positivo por explicar esto. - Piotr Dobrogost

Como una extensión de respuesta aceptada Oracle utiliza internamente ROW_NUMBER/RANK funciones. OFFSET FETCH la sintaxis es un azúcar de sintaxis.

Podría observarse usando DBMS_UTILITY.EXPAND_SQL_TEXT procedimiento:

Preparando muestra:

CREATE TABLE rownum_order_test (
  val  NUMBER
);

INSERT ALL
  INTO rownum_order_test
SELECT level
FROM   dual
CONNECT BY level <= 10;
COMMIT;

consulta:

SELECT val
FROM   rownum_order_test
ORDER BY val DESC
FETCH FIRST 5 ROWS ONLY;

es regular:

SELECT "A1"."VAL" "VAL" 
FROM  (SELECT "A2"."VAL" "VAL","A2"."VAL" "rowlimit_$_0",
               ROW_NUMBER() OVER ( ORDER BY "A2"."VAL" DESC ) "rowlimit_$$_rownumber" 
      FROM "ROWNUM_ORDER_TEST" "A2") "A1" 
WHERE "A1"."rowlimit_$$_rownumber"<=5 ORDER BY "A1"."rowlimit_$_0" DESC;

db <> demostración de violín

Obteniendo texto SQL expandido:

declare
  x VARCHAR2(1000);
begin
 dbms_utility.expand_sql_text(
        input_sql_text => '
          SELECT val
          FROM   rownum_order_test
          ORDER BY val DESC
          FETCH FIRST 5 ROWS ONLY',
        output_sql_text => x);

  dbms_output.put_line(x);
end;
/

WITH TIES se expande como RANK:

declare
  x VARCHAR2(1000);
begin
 dbms_utility.expand_sql_text(
        input_sql_text => '
          SELECT val
          FROM   rownum_order_test
          ORDER BY val DESC
          FETCH FIRST 5 ROWS WITH TIES',
        output_sql_text => x);

  dbms_output.put_line(x);
end;
/

SELECT "A1"."VAL" "VAL" 
FROM  (SELECT "A2"."VAL" "VAL","A2"."VAL" "rowlimit_$_0",
              RANK() OVER ( ORDER BY "A2"."VAL" DESC ) "rowlimit_$$_rank" 
       FROM "ROWNUM_ORDER_TEST" "A2") "A1" 
WHERE "A1"."rowlimit_$$_rank"<=5 ORDER BY "A1"."rowlimit_$_0" DESC

y compensar:

declare
  x VARCHAR2(1000);
begin
 dbms_utility.expand_sql_text(
        input_sql_text => '
          SELECT val
FROM   rownum_order_test
ORDER BY val
OFFSET 4 ROWS FETCH NEXT 4 ROWS ONLY',
        output_sql_text => x);

  dbms_output.put_line(x);
end;
/


SELECT "A1"."VAL" "VAL" 
FROM  (SELECT "A2"."VAL" "VAL","A2"."VAL" "rowlimit_$_0",
             ROW_NUMBER() OVER ( ORDER BY "A2"."VAL") "rowlimit_$$_rownumber" 
       FROM "ROWNUM_ORDER_TEST" "A2") "A1" 
       WHERE "A1"."rowlimit_$$_rownumber"<=CASE  WHEN (4>=0) THEN FLOOR(TO_NUMBER(4)) 
             ELSE 0 END +4 AND "A1"."rowlimit_$$_rownumber">4 
ORDER BY "A1"."rowlimit_$_0"

Respondido 18 ago 19, 20:08

Menos declaraciones SELECT. Además, consume menos rendimiento. Créditos a: anibal@upf.br

SELECT *
    FROM   (SELECT t.*,
                   rownum AS rn
            FROM   shhospede t) a
    WHERE  a.rn >= in_first
    AND    a.rn <= in_first;

respondido 02 mar '11, 14:03

Además, es una respuesta totalmente incorrecta. La pregunta trataba sobre la limitación DESPUÉS de la clasificación. Entonces, rownum debería estar fuera de la subconsulta. - BitLord

Si no está en Oracle 12C, puede usar la consulta TOP N como se muestra a continuación.

SELECT *
 FROM
   ( SELECT rownum rnum
          , a.*
       FROM sometable a 
   ORDER BY name
   )
WHERE rnum BETWEEN 10 AND 20;

Incluso puede mover esto de la cláusula a la cláusula de la siguiente manera

WITH b AS
( SELECT rownum rnum
      , a.* 
   FROM sometable a ORDER BY name
) 
SELECT * FROM b 
WHERE rnum BETWEEN 10 AND 20;

En realidad, aquí estamos creando una vista en línea y cambiando el nombre de rownum a rnum. Puede utilizar rnum en la consulta principal como criterio de filtrado.

Respondido el 08 de diciembre de 16 a las 10:12

En mi caso, esto no devolvió las filas correctas. Lo que hice para arreglarlo fue hacer el ORDER BY y rownum por separado. Básicamente, creé una subconsulta que tenía el ORDER BY cláusula. - Patricio Gregorio

Votar en contra ya que es una respuesta incorrecta. La pregunta se trataba de limitar después de la clasificación, por lo que rownum debe estar fuera de una subconsulta. - Piotr Dobrogost

@PiotrDobrogost rownum solo está afuera. - Sandi

Comencé a prepararme para el examen Oracle 1z0-047, validado contra 12c. Mientras me preparaba, encontré una mejora de 12c conocida como 'FETCH FIRST'. Le permite buscar filas / limitar filas según su conveniencia. Varias opciones están disponibles con él

- FETCH FIRST n ROWS ONLY
 - OFFSET n ROWS FETCH NEXT N1 ROWS ONLY // leave the n rows and display next N1 rows
 - n % rows via FETCH FIRST N PERCENT ROWS ONLY

Ejemplo:

Select * from XYZ a
order by a.pqr
FETCH FIRST 10 ROWS ONLY

Respondido el 01 de junio de 16 a las 11:06

stackoverflow.com/a/26051830/635608 - esto ya se ha proporcionado en otras respuestas. Abstente de publicar cosas que ya se hayan publicado hace meses. - Sabanilla

Oh, claro, no revisé todas las respuestas, encontré las subconsultas al principio, lo tendré en cuenta. - Arjun Gaur

select * FROM (SELECT 
   ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID, 
 FROM EMP ) EMP  where ROWID=5

mayores que los valores averigüen

select * FROM (SELECT 
       ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID, 
     FROM EMP ) EMP  where ROWID>5

menos que los valores averigüen

select * FROM (SELECT 
       ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID, 
     FROM EMP ) EMP  where ROWID=5

Respondido 12 Abr '17, 12:04

Votar en contra como ROW_NUMBER() La solución basada ya había sido publicada por Leigh Riffel. Además, hay errores de sintaxis en el código que se muestra. - Piotr Dobrogost

Para cada fila devuelta por una consulta, la pseudocolumna ROWNUM devuelve un número que indica el orden en el que Oracle selecciona la fila de una tabla o un conjunto de filas unidas. La primera fila seleccionada tiene un ROWNUM de 1, la segunda tiene 2 y así sucesivamente.

  SELECT * FROM sometable1 so
    WHERE so.id IN (
    SELECT so2.id from sometable2 so2
    WHERE ROWNUM <=5
    )
    AND ORDER BY so.somefield AND ROWNUM <= 100 

He implementado esto en oracle servidor 11.2.0.1.0

respondido 13 mar '19, 08:03

Votar en contra ya que la pregunta se refiere a limitar ordenado filas y ni siquiera tienes orden - Piotr Dobrogost

@PiotrDobrogost Entiende que no es una gran tarea, ordenar las palabras clave son comunes para todos los rdbms, solo el límite tiene cambios. - Sumesh TG

En el caso de SQL-Developer, obtiene automáticamente solo las primeras 50 filas. ¡Y si nos desplazamos hacia abajo, obtiene otras 50 filas y así sucesivamente!

Por lo tanto, no necesitamos definir, en el caso de la herramienta sql-developer.

contestado el 06 de mayo de 20 a las 19:05

(no probado) algo como esto puede hacer el trabajo

WITH
base AS
(
    select *                   -- get the table
    from sometable
    order by name              -- in the desired order
),
twenty AS
(
    select *                   -- get the first 30 rows
    from base
    where rownum < 30
    order by name              -- in the desired order
)
select *                       -- then get rows 21 .. 30
from twenty
where rownum > 20
order by name                  -- in the desired order

También está el rango de la función analítica, que puede usar para ordenar.

Respondido el 22 de enero de 09 a las 20:01

Esto no devolverá una sola fila ya que ROWNUM es una columna en el conjunto de resultados, por lo que la última condición WHERE siempre será falsa. Además, no puede usar ROWNUM y un PEDIDO POR PEDIDO de garantía. - paquet

Excelente. Dejemos esto aquí como una advertencia para los demás. - maldadenseñar

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.