¿Cómo limito el número de filas devueltas por una consulta de Oracle después de realizar el pedido?
Frecuentes
Visto 1,597 equipos
1099
¿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.
15 Respuestas
696
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 N
th 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
809
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
187
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
55
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
28
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
19
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
14
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
6
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;
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
5
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
3
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
2
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
1
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
1
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
-3
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
-4
(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 sql oracle pagination sql-limit or haz tu propia pregunta.
Estandarizado en SQL: 2008. - dalle
El límite fue anunciado por Tom Kyte para Oracle 12c ... - wolφi
¿Obteniendo la página siguiente en un conjunto de resultados? - Mathieu Longtin
@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. - jpmc26
@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. - jpmc26