Existe-t-il un moyen de faire en sorte qu'une requête Oracle
se comporte comme si elle contenait une clause MySQL limit
?
Dans MySQL
, je peux faire ceci:
select *
from sometable
order by name
limit 20,10
pour obtenir les rangées 21 à 30 (sautez les 20 premiers, donnez les 10 suivants). Les lignes sont sélectionnées après le order by
, donc le 20ème nom commence réellement par ordre alphabétique.
Dans Oracle
, la seule chose que les gens mentionnent est la pseudo-colonne rownum
, mais elle est évaluée avantorder by
, ce qui signifie que:
select *
from sometable
where rownum <= 10
order by name
renverra un ensemble aléatoire de dix lignes ordonnées par nom, ce qui n'est généralement pas ce que je veux. Cela ne permet pas non plus de spécifier un décalage.
À partir d'Oracle 12c R1 (12.1), là est un clause de limitation de ligne . Il n'utilise pas la syntaxe familière LIMIT
, mais il peut mieux faire le travail avec plus d'options. Vous pouvez trouver le syntaxe complète ici .
Pour répondre à la question d'origine, voici la requête:
SELECT *
FROM sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
(Pour les versions antérieures d'Oracle, veuillez vous reporter aux autres réponses à cette question.)
Les exemples suivants ont été cités dans page liée , dans l’espoir d’empêcher la pourriture des liens.
CREATE TABLE rownum_order_test (
val NUMBER
);
INSERT ALL
INTO rownum_order_test
SELECT level
FROM dual
CONNECT BY level <= 10;
COMMIT;
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.
N
lignesSELECT val
FROM rownum_order_test
ORDER BY val DESC
FETCH FIRST 5 ROWS ONLY;
VAL
----------
10
10
9
9
8
5 rows selected.
N
lignes, si N
th la ligne a des liens, obtenez toutes les lignes attachéesSELECT 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.
x
% de lignesSELECT val
FROM rownum_order_test
ORDER BY val
FETCH FIRST 20 PERCENT ROWS ONLY;
VAL
----------
1
1
2
2
4 rows selected.
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.
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.
Vous pouvez utiliser une sous-requête comme ceci
select *
from
( select *
from emp
order by sal desc )
where ROWNUM <= 5;
Consultez également le sujet Sur ROWNUM et limitation des résultats à Oracle/AskTom pour plus d'informations.
Update : Pour limiter le résultat avec des limites inférieures et supérieures, les choses deviennent un peu plus lourdes.
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;
(Copié à partir de l'article spécifié AskTom)
Mise à jour 2 : à partir de Oracle 12c (12.1), une syntaxe est disponible pour limiter les lignes ou commencer aux décalages.
SELECT *
FROM sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
Voir cette réponse pour plus d'exemples. Merci à Krumia pour cet indice.
J'ai effectué des tests de performance pour les approches suivantes:
select * from (
select a.*, ROWNUM rnum from (
<select statement with order by clause>
) a where rownum <= MAX_ROW
) where rnum >= MIN_ROW
select * from (
<select statement with order by clause>
) where myrow between MIN_ROW and MAX_ROW
select * from (
select statement, rownum as RN with order by clause
) where a.rn >= MIN_ROW and a.rn <= MAX_ROW
La table avait 10 millions d’enregistrements, le tri était sur une ligne datetime non indexée:
La sélection des 10 premières lignes a pris:
Sélection de lignes entre 100 000 et 100 010:
Sélection de lignes entre 9 000 000 et 9 000 010:
Une solution analytique avec une seule requête imbriquée:
SELECT * FROM
(
SELECT t.*, Row_Number() OVER (ORDER BY name) MyRow FROM sometable t
)
WHERE MyRow BETWEEN 10 AND 20;
Rank()
peut être remplacé par Row_Number()
, mais peut renvoyer plus d'enregistrements que prévu si le nom contient des valeurs en double.
Sur Oracle 12c (voir la clause de limitation de ligne dans référence SQL ):
SELECT *
FROM sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
Les requêtes de pagination avec commande sont très délicates dans Oracle.
Oracle fournit une pseudocolonne ROWNUM qui renvoie un nombre indiquant l'ordre dans lequel la base de données sélectionne la ligne dans une table ou un ensemble de vues jointes.
ROWNUM est une pseudo-colonne qui cause des problèmes à beaucoup de gens. Une valeur ROWNUM n'est pas affectée de manière permanente à une ligne (il s'agit d'un malentendu courant). Cela peut être déroutant quand une valeur ROWNUM est réellement assignée. Une valeur ROWNUM est attribuée à une ligne après avoir passé les prédicats de filtre de la requête mais avant l'agrégation ou le tri de la requête .
De plus, une valeur ROWNUM est incrémentée uniquement après son affectation.
C'est pourquoi la requête suivante ne renvoie aucune ligne:
select *
from (select *
from some_table
order by some_column)
where ROWNUM <= 4 and ROWNUM > 1;
La première ligne du résultat de la requête ne transmet pas le prédicat ROWNUM> 1; par conséquent, ROWNUM n'augmente pas à 2. Pour cette raison, aucune valeur ROWNUM ne dépasse 1, par conséquent, la requête ne renvoie aucune ligne.
Requête correctement définie devrait ressembler à ceci:
select *
from (select *, ROWNUM rnum
from (select *
from skijump_results
order by points)
where ROWNUM <= 4)
where rnum > 1;
En savoir plus sur les requêtes de pagination dans mes articles sur Vertabelo blog:
Moins d'instructions SELECT. En outre, consommant moins de performances Crédits à: [email protected]
SELECT *
FROM (SELECT t.*,
rownum AS rn
FROM shhospede t) a
WHERE a.rn >= in_first
AND a.rn <= in_first;
Si vous n'êtes pas sur Oracle 12C, vous pouvez utiliser la requête TOP N comme ci-dessous.
SELECT *
FROM
( SELECT rownum rnum
, a.*
FROM sometable a
ORDER BY name
)
WHERE rnum BETWEEN 10 AND 20;
Vous pouvez même déplacer ceci de clause en clause avec comme suit
WITH b AS
( SELECT rownum rnum
, a.*
FROM sometable a ORDER BY name
)
SELECT * FROM b
WHERE rnum BETWEEN 10 AND 20;
Ici, en fait, nous créons une vue en ligne et renommons rownum en tant que nom. Vous pouvez utiliser rnum dans la requête principale comme critère de filtrage.
En tant qu'extension de réponse acceptée , Oracle utilise en interne les fonctions _ROW_NUMBER/RANK
_. La syntaxe _OFFSET FETCH
_ est un sucre de syntaxe.
Pour l'observer, utilisez la procédure DBMS_UTILITY.EXPAND_SQL_TEXT
:
Préparation de l'échantillon:
_CREATE TABLE rownum_order_test (
val NUMBER
);
INSERT ALL
INTO rownum_order_test
SELECT level
FROM dual
CONNECT BY level <= 10;
COMMIT;
_
Requete:
_SELECT val
FROM rownum_order_test
ORDER BY val DESC
FETCH FIRST 5 ROWS ONLY;
_
est régulier:
_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;
_
Récupération du texte SQL développé:
_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
_ est développé en tant que 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
_
et offset:
_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"
_
select * FROM (SELECT
ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID,
FROM EMP ) EMP where ROWID=5
plus alors les valeurs découvrent
select * FROM (SELECT
ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID,
FROM EMP ) EMP where ROWID>5
moins que les valeurs découvrent
select * FROM (SELECT
ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID,
FROM EMP ) EMP where ROWID=5
Comme je l'ai expliqué dans cet article , SQL: 2008 Standard fournit la syntaxe suivante pour limiter le jeu de résultats SQL:
SELECT
title
FROM
post
ORDER BY
id DESC
FETCH FIRST 50 ROWS ONLY
Avant la version 12c, pour récupérer les enregistrements Top-N, vous deviez utiliser une table dérivée et la pseudocolonne ROWNUM:
SELECT *
FROM (
SELECT
title
FROM
post
ORDER BY
id DESC
)
WHERE ROWNUM <= 50
J'ai commencé à me préparer pour l'examen Oracle 1z0-047, validé contre 12c. Tout en me préparant, je suis tombé sur une amélioration 12c connue sous le nom de 'FETCH FIRST'. Elle vous permet d'extraire des rangées/nombre limite de rangées selon votre convenance. Plusieurs options sont disponibles avec elle
- 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
Exemple:
Select * from XYZ a
order by a.pqr
FETCH FIRST 10 ROWS ONLY
Pour chaque ligne renvoyée par une requête, la pseudocolonne ROWNUM renvoie un nombre indiquant l'ordre dans lequel Oracle sélectionne la ligne dans une table ou un ensemble de lignes jointes. La première ligne sélectionnée a un ROWNUM de 1, la seconde 2 et ainsi de suite.
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
J'ai implémenté ceci dans Oracle
serveur 11.2.0.1.0