web-dev-qa-db-fra.com

Comment limiter le nombre de lignes renvoyées par une requête Oracle après la commande?

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.

946
Mathieu Longtin

À 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.)


Exemples:

Les exemples suivants ont été cités dans page liée , dans l’espoir d’empêcher la pourriture des liens.

Installer

CREATE TABLE rownum_order_test (
  val  NUMBER
);

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

COMMIT;

Qu'y a-t-il dans la table?

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.

Obtenir les premières N lignes

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

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

5 rows selected.

Obtenir les premières N lignes, si Nth la ligne a des liens, obtenez toutes les lignes attachées

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.

Top x% de lignes

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

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

4 rows selected.

Utiliser un offset, très utile pour la pagination

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.

Vous pouvez combiner décalage avec des pourcentages

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.
495
sampathsris

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.

763
Kosi2801

J'ai effectué des tests de performance pour les approches suivantes:

Asktom

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

Analytique

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

Alternative courte

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

Résultats

La table avait 10 millions d’enregistrements, le tri était sur une ligne datetime non indexée:

  • Le plan Explain montre la même valeur pour les trois sélections (323168)
  • Mais le gagnant est AskTom (suivi analytique de près)

La sélection des 10 premières lignes a pris:

  • AskTom: 28-30 secondes
  • Analytique: 33-37 secondes
  • Alternative courte: 110-140 secondes

Sélection de lignes entre 100 000 et 100 010:

  • AskTom: 60 secondes
  • Analytique: 100 secondes

Sélection de lignes entre 9 000 000 et 9 000 010:

  • AskTom: 130 secondes
  • Analytique: 150 secondes
176
zeldi

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.

53
Leigh Riffel

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;
28
beldaz

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:

13
Bartek

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;
5

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.

2
sandi

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;
_

db <> démo fiddle

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"
_
2
Lukasz Szozda
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
2
Mehul Akabari

Standard SQL

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

Oracle 11g et versions antérieures

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
1
Vlad Mihalcea

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
1
arjun gaur

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

0
Sumesh TG