Paging mit Oracle

97

Ich bin mit Oracle nicht so vertraut, wie ich es gerne hätte. Ich habe ungefähr 250.000 Datensätze und möchte sie 100 pro Seite anzeigen. Derzeit habe ich eine gespeicherte Prozedur, die alle eine Viertelmillion Datensätze mithilfe eines Datenadapters und eines Datensatzes sowie der Methode dataadapter.Fill (Datensatz) für die Ergebnisse des gespeicherten Prozesses in einen Datensatz abruft. Wenn ich "Seitenzahl" und "Anzahl der Datensätze pro Seite" als ganzzahlige Werte habe, die ich als Parameter übergeben kann, ist dies der beste Weg, um nur diesen bestimmten Abschnitt zurückzugewinnen. Sagen wir, wenn ich 10 als Seitenzahl und 120 als Seitenzahl übergebe, würde es mir aus der select-Anweisung den 1880. bis 1200. geben, oder so ähnlich, meine Mathematik in meinem Kopf könnte aus sein.

Ich mache das in .NET mit C #, dachte, das ist nicht wichtig. Wenn ich es auf der SQL-Seite richtig machen kann, sollte ich cool sein.

Update: Ich konnte Brians Vorschlag verwenden und er funktioniert hervorragend. Ich würde gerne an einer Optimierung arbeiten, aber die Seiten werden in 4 bis 5 Sekunden anstatt in einer Minute angezeigt, und meine Paging-Steuerung konnte sich sehr gut in meine neuen gespeicherten Prozesse integrieren.

stephenbayer
quelle

Antworten:

143

So etwas sollte funktionieren: Aus Frans Boumas Blog

SELECT * FROM
(
    SELECT a.*, rownum r__
    FROM
    (
        SELECT * FROM ORDERS WHERE CustomerID LIKE 'A%'
        ORDER BY OrderDate DESC, ShippingDate DESC
    ) a
    WHERE rownum < ((pageNumber * pageSize) + 1 )
)
WHERE r__ >= (((pageNumber-1) * pageSize) + 1)
Brian Schmitt
quelle
4
Ja, es ist eine eingebaute Spalte, die Oracle unterstützt. Sie beginnt immer bei 1 und erhöht sich für jede Zeile. Wenn Sie in diesem Codeausschnitt 1000 Zeilen haben, wird die Sortierreihenfolge angewendet und jeder Zeile wird ein Rownum zugewiesen. Die äußeren Auswahlen verwenden diese Zeilennummern, um die gesuchte 'Seite' basierend auf Ihrer Seitengröße zu finden.
Brian Schmitt
9
Dies ist nett, aber bei großen Auswahlen schrecklich langsam. Überprüfen Sie einfach, wann 0 bis 1000 und 500.000 bis 501.000 ausgewählt werden müssen. Ich habe diese Art von Auswahlstruktur verwendet und suche jetzt nach einer Problemumgehung.
Newhouse
3
@ n3whous3 Sie könnten dies versuchen - inf.unideb.hu/~gabora/pagination/results.html
jasonk
7
Ich fragte mich, warum zwei WHEREnicht kombiniert werden konnten AND, und fand dies dann: orafaq.com/wiki/ROWNUM
Mengdi Gao
1
Die Oracle-Paginierung ruiniert meinen Tag.
Ätherus
133

Fragen Sie Tom nach Paginierung und sehr, sehr nützlichen Analysefunktionen.

Dies ist ein Auszug aus dieser Seite:

select * from (
    select /*+ first_rows(25) */
     object_id,object_name,
     row_number() over
    (order by object_id) rn
        from all_objects)
    where rn between :n and :m
        order by rn;
Chobicus
quelle
7
Dies ist tatsächlich eine viel bessere Implementierung, obwohl es schwierig ist, sie in diesem Beitrag zu finden. Wenn Sie viele große Seiten haben, muss die andere Antwort auch alle Zeilen der vorherigen Seiten durchlaufen. Bei komplizierten Abfragen bedeutet dies, dass spätere Seiten schlechter abschneiden als frühere Seiten.
Tallseth
@ Tallseth Du hast recht. Es ist schwer, es auf dieser Seite zu finden. Auszug wird hinzugefügt.
Chobicus
Dies ist die richtige Antwort, wenn Sie Ihre Bestellung dynamisch ändern möchten.
Chakeda
74

Der Vollständigkeit halber gibt es in Oracle 12c für Benutzer, die nach einer moderneren Lösung suchen, einige neue Funktionen, darunter besseres Paging und Top-Handling.

Paging

Das Paging sieht folgendermaßen aus:

SELECT *
FROM user
ORDER BY first_name
OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY;

Top N Rekorde

Das Abrufen der Top-Rekorde sieht folgendermaßen aus:

SELECT *
FROM user
ORDER BY first_name
FETCH FIRST 5 ROWS ONLY

Beachten Sie, dass beide obigen Abfragebeispiele ORDER BYKlauseln enthalten. Die neuen Befehle berücksichtigen diese und werden für die sortierten Daten ausgeführt.

Ich konnte keine gute Oracle-Referenzseite für FETCHoder finden, OFFSETaber diese Seite bietet einen großartigen Überblick über diese neuen Funktionen.

Performance

Wie @wweicker in den Kommentaren unten hervorhebt, ist die Leistung ein Problem mit der neuen Syntax in 12c. Ich hatte keine Kopie von 18c zum Testen, ob Oracle sie seitdem verbessert hat.

Interessanterweise wurden meine tatsächlichen Ergebnisse etwas schneller zurückgegeben, als ich die Abfragen für die neue Methode zum ersten Mal in meiner Tabelle (113 Millionen + Zeilen) ausführte:

  • Neue Methode: 0,013 Sekunden.
  • Alte Methode: 0,107 Sekunden.

Wie @wweicker bereits erwähnte, sieht der EXPLAIN-Plan für die neue Methode jedoch viel schlechter aus:

  • Kosten für neue Methoden: 300.110
  • Kosten der alten Methode: 30

Die neue Syntax führte zu einem vollständigen Scan des Index in meiner Spalte, was die gesamten Kosten darstellte. Es besteht die Möglichkeit, dass sich die Situation bei der Beschränkung auf nicht indizierte Daten erheblich verschlechtert.

Lassen Sie uns einen Blick darauf werfen, wenn Sie eine einzelne nicht indizierte Spalte in den vorherigen Datensatz aufnehmen:

  • Zeit / Kosten der neuen Methode: 189,55 Sekunden / 998.908
  • Zeit / Kosten der alten Methode: 1,973 Sekunden / 256

Zusammenfassung: Seien Sie vorsichtig, bis Oracle diese Behandlung verbessert. Wenn Sie einen Index haben, mit dem Sie arbeiten können, können Sie möglicherweise mit der neuen Methode davonkommen.

Hoffentlich habe ich bald eine Kopie von 18c zum Spielen und kann sie aktualisieren

JoelC
quelle
Dies ist eine großartige Antwort für 12c-Benutzer
Lalji Gajera
1
Die Syntax ist sauberer, aber die Leistung ist schlechter ( dba-present.com/index.php/databases/oracle/… )
wweicker
Gut zu wissen, danke @wweicker. Hoffentlich wird die Leistung bald von Oracle behoben. Wenn man Oracle kennt, könnte dies eine ferne Hoffnung sein!
JoelC
Die Syntax ist neu und wird in reguläre ROW_NUMBER / RANK-Aufrufe umgewandelt. Verwandte Wie kann ich die Anzahl der von einer Oracle-Abfrage nach der Bestellung zurückgegebenen Zeilen begrenzen?
Lukasz Szozda
@JoelC Wurden Ihre Meinungen geändert?
Ryan
11

Ich möchte nur die Antworten und Kommentare zusammenfassen. Es gibt verschiedene Möglichkeiten, eine Paginierung durchzuführen.

Vor Oracle 12c gab es keine OFFSET / FETCH-Funktionalität. Schauen Sie sich also das Whitepaper an, wie von @jasonk vorgeschlagen. Es ist der vollständigste Artikel, den ich über verschiedene Methoden mit einer detaillierten Erklärung der Vor- und Nachteile gefunden habe. Das Kopieren und Einfügen würde viel Zeit in Anspruch nehmen, daher werde ich es nicht tun.

Es gibt auch einen guten Artikel von Joooq-Entwicklern, in dem einige häufige Einschränkungen bei der Paginierung von Orakeln und anderen Datenbanken erläutert werden. Joqs Blogpost

Gute Nachrichten, seit Oracle 12c haben wir eine neue OFFSET / FETCH-Funktionalität. Neue Funktionen von OracleMagazine 12c . Weitere Informationen finden Sie unter "Top-N-Abfragen und Paginierung".

Sie können Ihre Oracle-Version überprüfen, indem Sie die folgende Anweisung ausgeben

SELECT * FROM V$VERSION
Vadim Kirilchuk
quelle
7

Versuche Folgendes:

SELECT *
FROM
  (SELECT FIELDA,
    FIELDB,
    FIELDC,
    ROW_NUMBER() OVER (ORDER BY FIELDC) R
  FROM TABLE_NAME
  WHERE FIELDA = 10
  )
WHERE R >= 10
AND R   <= 15;

über [tecnicume]

Furetto
quelle
0

In meinem Projekt habe ich Oracle 12c und Java verwendet . Der Paging-Code sieht folgendermaßen aus:

 public public List<Map<String, Object>> getAllProductOfferWithPagination(int pageNo, int pageElementSize, Long productOfferId, String productOfferName) {
    try {

        if(pageNo==1){
            //do nothing
        } else{
            pageNo=(pageNo-1)*pageElementSize+1;
        }
        System.out.println("algo pageNo: " + pageNo +"  pageElementSize: "+ pageElementSize+"  productOfferId: "+ productOfferId+"  productOfferName: "+ productOfferName);

        String sql = "SELECT * FROM ( SELECT * FROM product_offer po WHERE po.deleted=0 AND (po.product_offer_id=? OR po.product_offer_name LIKE ? )" +
             " ORDER BY po.PRODUCT_OFFER_ID asc) foo OFFSET ? ROWS FETCH NEXT ? ROWS ONLY ";

       return jdbcTemplate.queryForList(sql,new Object[] {productOfferId,"%"+productOfferName+"%",pageNo-1, pageElementSize});

    } catch (Exception e) {
        System.out.println(e);
        e.printStackTrace();
        return null;
    }
Ferdous Wahid
quelle