web-dev-qa-db-de.com

Wie beschränke ich die Anzahl der Zeilen, die von einer Oracle-Abfrage nach der Bestellung zurückgegeben werden?

Gibt es eine Möglichkeit, eine Oracle-Abfrage so zu gestalten, als ob sie eine MySQL limit-Klausel enthält?

In MySQL kann ich Folgendes tun:

select * 
from sometable
order by name
limit 20,10

um die 21. bis 30. Reihe zu erreichen (überspringe die ersten 20, gib die nächsten 10). Die Zeilen werden nach dem order by ausgewählt, sodass es wirklich mit dem 20. Namen alphabetisch beginnt.

In Oracle wird nur die rownum -Pseudospalte erwähnt, sie wird jedoch ausgewertet vororder by, was Folgendes bedeutet:

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

gibt eine zufällige Menge von zehn Zeilen zurück, die nach Namen sortiert sind. Dies ist normalerweise nicht meine Absicht. Es ist auch nicht möglich, einen Offset anzugeben.

946
Mathieu Longtin

Ab Oracle 12c R1 (12.1) gibt es eine Zeilenbegrenzungsklausel . Es verwendet keine vertraute LIMIT -Syntax, kann jedoch mit mehr Optionen besser arbeiten. Sie finden die vollständige Syntax hier .

Um die ursprüngliche Frage zu beantworten, ist hier die Abfrage:

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

(Für frühere Oracle-Versionen lesen Sie bitte die anderen Antworten in dieser Frage.)


Beispiele:

Die folgenden Beispiele wurden aus verlinkte Seite zitiert, in der Hoffnung, das Verrotten von Links zu verhindern.

Konfiguration

CREATE TABLE rownum_order_test (
  val  NUMBER
);

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

COMMIT;

Was ist in der Tabelle?

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.

Holen Sie sich die ersten N Zeilen

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

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

5 rows selected.

Erste N Zeilen abrufen, wenn Nth Reihe hat Bindungen, alle gebundenen Reihen erhalten

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% der Zeilen

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

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

4 rows selected.

Mit einem Offset, sehr nützlich für die Paginierung

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.

Sie können Offset mit Prozentsätzen kombinieren

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

Dazu können Sie eine Unterabfrage verwenden

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

Weitere Informationen finden Sie im Thema Auf ROWNUM und Einschränkung der Ergebnisse unter Oracle/AskTom.

Update : Um das Ergebnis sowohl bei der unteren als auch bei der oberen Grenze einzugrenzen, wird es etwas aufgeblähter

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;

(Kopiert von angegebenem AskTom-Artikel)

Update 2 : Ab Oracle 12c (12.1) steht eine Syntax zur Verfügung, mit der Zeilen begrenzt oder bei Offsets begonnen werden können.

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

Siehe diese Antwort für weitere Beispiele. Danke an Krumia für den Hinweis.

763
Kosi2801

Ich habe einige Leistungstests für die folgenden Ansätze durchgeführt:

Asktom

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

Analytische

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

Kurze Alternative

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

Ergebnisse

Die Tabelle hatte 10 Millionen Datensätze. Die Sortierung erfolgte in einer nicht indizierten datetime-Zeile:

  • Der Erklärungsplan ergab für alle drei Auswahlen den gleichen Wert (323168).
  • Aber der Gewinner ist AskTom (mit analytischer Nachfolge dicht dahinter)

Die Auswahl der ersten 10 Zeilen dauerte:

  • AskTom: 28-30 Sekunden
  • Analytisch: 33-37 Sekunden
  • Kurze Alternative: 110-140 Sekunden

Zeilen zwischen 100.000 und 100.010 auswählen:

  • AskTom: 60 Sekunden
  • Analytisch: 100 Sekunden

Zeilen zwischen 9.000.000 und 9.000.010 auswählen:

  • AskTom: 130 Sekunden
  • Analytisch: 150 Sekunden
176
zeldi

Eine analytische Lösung mit nur einer verschachtelten Abfrage:

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

Rank() könnte Row_Number() ersetzen, aber möglicherweise mehr Datensätze zurückgeben, als Sie erwarten, wenn doppelte Werte für name vorhanden sind.

53
Leigh Riffel

Unter Oracle 12c (siehe Zeilenbeschränkungsklausel in SQL-Referenz ):

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

Paginierungsabfragen bei der Bestellung sind in Oracle sehr schwierig.

Oracle stellt eine ROWNUM-Pseudospalte bereit, die eine Zahl zurückgibt, die die Reihenfolge angibt, in der die Datenbank die Zeile aus einer Tabelle oder einer Gruppe von verbundenen Ansichten auswählt.

ROWNUM ist eine Pseudosäule, die viele Menschen in Schwierigkeiten bringt. Ein ROWNUM-Wert ist keiner Zeile fest zugeordnet (dies ist ein häufiges Missverständnis). Es kann verwirrend sein, wenn tatsächlich ein ROWNUM-Wert zugewiesen wird. Ein ROWNUM-Wert wird einer Zeile zugewiesen, nachdem er Filterprädikate der Abfrage passiert hat, jedoch , bevor die Abfrage aggregiert oder sortiert wird .

Darüber hinaus wird ein ROWNUM-Wert erst erhöht, nachdem er zugewiesen wurde.

Aus diesem Grund gibt die folgende Abfrage keine Zeilen zurück:

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

In der ersten Zeile des Abfrageergebnisses wird das Prädikat ROWNUM> 1 nicht übergeben, sodass ROWNUM nicht auf 2 erhöht wird. Aus diesem Grund wird kein ROWNUM-Wert größer als 1, sodass die Abfrage keine Zeilen zurückgibt.

Eine korrekt definierte Abfrage sollte folgendermaßen aussehen:

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

Weitere Informationen zu Paginierungsabfragen finden Sie in meinen Artikeln im Vertabelo Blog:

13
Bartek

Weniger SELECT-Anweisungen. Auch weniger leistungsaufwendig. Credits an: [email protected]

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

Wenn Sie nicht mit Oracle 12C arbeiten, können Sie die nachstehende TOP N-Abfrage verwenden.

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

Sie können diese from-Klausel mit der folgenden Klausel sogar verschieben

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

Hier erstellen wir eine Inline-Ansicht und benennen rownum in rnum um. Sie können rnum in der Hauptabfrage als Filterkriterium verwenden.

2
sandi

Als Erweiterung von akzeptierte Antwort verwendet Oracle intern _ROW_NUMBER/RANK_ -Funktionen. _OFFSET FETCH_ Syntax ist ein Syntaxzucker.

Dies kann mithilfe der DBMS_UTILITY.EXPAND_SQL_TEXT -Prozedur beobachtet werden:

Probe vorbereiten:

_CREATE TABLE rownum_order_test (
  val  NUMBER
);

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

Abfrage:

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

ist regelmäßig:

_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 <> fiddle demo

Erweiterten SQL-Text abrufen:

_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_ wird erweitert als 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
_

und 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

größer als Werte herausfinden

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

weniger als Werte herausfinden

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

SQL Standard

Wie ich in diesem Artikel erklärt habe, enthält der SQL: 2008-Standard die folgende Syntax, um die SQL-Ergebnismenge zu begrenzen:

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

Oracle 11g und ältere Versionen

Vor Version 12c mussten Sie zum Abrufen der Top-N-Datensätze eine abgeleitete Tabelle und die Pseudospalte ROWNUM verwenden:

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

Ich habe angefangen, mich auf die Oracle 1z0-047-Prüfung vorzubereiten, die gegen 12c validiert wurde. Während der Vorbereitung stieß ich auf eine 12c-Verbesserung, die als 'FETCH FIRST' bekannt ist. Es stehen mehrere Optionen zur Verfügung

- 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

Beispiel:

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

Für jede Zeile, die von einer Abfrage zurückgegeben wird, gibt die ROWNUM-Pseudospalte eine Zahl zurück, die die Reihenfolge angibt, in der Oracle die Zeile aus einer Tabelle oder einer Gruppe verbundener Zeilen auswählt. Die erste ausgewählte Zeile hat eine ROWNUM von 1, die zweite hat 2 und so weiter.

  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 

Ich habe dies in Oracle Server 11.2.0.1.0 implementiert

0
Sumesh TG