1367 Stimmen

Wie kann ich die Anzahl der von einer Oracle-Abfrage zurückgegebenen Zeilen nach der Bestellung begrenzen?

Gibt es eine Möglichkeit, eine Oracle Abfrage so verhalten, als enthalte sie eine MySQL limit Klausel?

In MySQL kann ich dies tun:

select * 
from sometable
order by name
limit 20,10

um die 21. bis 30. Reihe zu erhalten (die ersten 20 überspringen, die nächsten 10 geben). Die Zeilen werden nach der order by Die Liste beginnt also wirklich mit dem 20. Namen in alphabetischer Reihenfolge.

In Oracle ist das einzige, was die Leute erwähnen, die rownum Pseudo-Spalte, aber sie wird ausgewertet vor order by Dies bedeutet Folgendes:

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

gibt eine zufällige Menge von zehn nach Namen geordneten Zeilen zurück, was normalerweise nicht das ist, was ich will. Außerdem ist es nicht möglich, einen Offset festzulegen.

21 Stimmen

Standardisiert in SQL:2008.

1 Stimmen

Es sieht so aus, als ob in Ihrem ersten MySQL-Beispiel Offset und Row_count vertauscht sind. Diese Abfrage wählt die Zeilen 11 bis 30 aus, nicht 21 bis 30.

17 Stimmen

Limit wurde von Tom Kyte für Oracle 12c angekündigt...

1039voto

Kosi2801 Punkte 20758

Sie können dafür eine Unterabfrage verwenden wie

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

Sehen Sie sich auch das Thema Über ROWNUM und begrenzte Ergebnisse bei Oracle/AskTom für weitere Informationen.

更新情報 : Um das Ergebnis sowohl nach unten als auch nach oben zu begrenzen, werden die Dinge etwas aufgebläht mit

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 aus dem angegebenen AskTom-Artikel)

Aktualisierung 2 : Ab Oracle 12c (12.1) gibt es eine Syntax, um Zeilen zu begrenzen oder mit Offsets zu beginnen.

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.

6 Stimmen

Dies ist definitiv der richtige Weg, aber seien Sie sich bewusst (wie der ask tom Artikel sagt), dass sich die Abfrageleistung mit zunehmender maximaler Rownum verschlechtert. Dies ist eine gute Lösung für Abfrageergebnisse, bei denen Sie nur die ersten paar Seiten sehen wollen, aber wenn Sie dies als Mechanismus für Code verwenden, um durch eine ganze Tabelle zu blättern, wäre es besser, Ihren Code zu überarbeiten

1 Stimmen

+1 Ihre untere/obere Version hat mir tatsächlich geholfen, ein Problem zu lösen, bei dem eine bloße obere begrenzte rownum-Klausel meine Abfrage drastisch verlangsamt hat.

2 Stimmen

Die Leigh Riffel "analytische Lösung mit nur einer verschachtelten Abfrage" ist die richtige.

198voto

zeldi Punkte 4783

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

Analytisch

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 nach einer nicht indizierten Datumszeile:

  • Erklären Sie den Plan zeigte den gleichen Wert für alle drei Auswahlen (323168)
  • Aber der Gewinner ist AskTom (dicht gefolgt von analytic)

Die Auswahl der ersten 10 Zeilen dauerte:

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

Auswahl von Zeilen zwischen 100.000 und 100.010:

  • AskTom: 60 Sekunden
  • Analytisch: 100 Sekunden

Auswahl von Zeilen zwischen 9.000.000 und 9.000.010:

  • AskTom: 130 Sekunden
  • Analytisch: 150 Sekunden

0 Stimmen

Gute Arbeit. Haben Sie die kurze Variante mit einem Zwischen anstelle von >= und <= ausprobiert?

5 Stimmen

@MathieuLongtin BETWEEN ist nur eine Kurzform für >= AND <= ( stackoverflow.com/questions/4809083/zwischen-klausel-versus-und )

1 Stimmen

Zeldi - Auf welcher Version war das? Oracle hat die analytische Leistung in 11.1. und 11.2. verbessert.

61voto

Vlad Mihalcea Punkte 121171

SQL-Standard

Seit Version 12c unterstützt Oracle den SQL:2008 Standard, der die folgende Syntax zur Begrenzung der SQL-Ergebnismenge bietet:

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

Oracle 11g und ältere Versionen

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

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

0 Stimmen

Ich bin neugierig auf, gab es nicht jemals eine Syntax in Oracle, die die Verwendung von "select TOP N * from {TableName}" oder so ähnlich unterstützt?

0 Stimmen

@Ak777 Nein. Das ist nur SQL Server.

60voto

Leigh Riffel Punkte 6073

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 ersetzt werden durch Row_Number() gibt aber möglicherweise mehr Datensätze zurück, als Sie erwarten, wenn es doppelte Werte für Name gibt.

3 Stimmen

Ich liebe die Analytik. Sie möchten vielleicht klären, was der Unterschied im Verhalten zwischen Rank() und Row_Number() sein würde.

0 Stimmen

In der Tat, ich weiß nicht, warum ich nicht an Duplikate gedacht habe. Also, in diesem Fall, wenn es doppelte Werte für Name gibt, dann könnte RANK mehr Datensätze liefern, als Sie erwarten, daher sollten Sie Row_Number verwenden.

0 Stimmen

Bei Erwähnung rank() Es ist auch erwähnenswert dense_rank() die für die Ausgabesteuerung nützlicher sein kann, da sie keine Zahlen "überspringt", während rank() kann. Auf jeden Fall für diese Frage row_number() ist am besten geeignet. Eine weitere Besonderheit ist, dass diese Technik auf jede Datenbank anwendbar ist, die die genannten Funktionen unterstützt.

33voto

beldaz Punkte 4009

Bei Oracle 12c (siehe Zeilenbegrenzungsklausel in SQL-Referenz ):

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

63 Stimmen

Und natürlich mussten sie eine völlig andere Syntax verwenden als alle anderen bisher

9 Stimmen

Nachdem wir uns mit allen anderen Anbietern zusammengesetzt hatten, um uns auf LIMIT in SQL:2008 mussten sie sich dann eine Scheibe von Microsoft abschneiden und den Standard brechen.

1 Stimmen

Interessanterweise habe ich kürzlich gehört, dass der jüngste Standard diese Syntax enthält, so dass Oracle sie vielleicht zuerst eingeführt hat, bevor es sie implementierte. Wahrscheinlich ist sie flexibler als LIMIT ... OFFSET

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X