Entspricht LIMIT für DB2

91

Wie geht das LIMITin DB2 für iSeries?

Ich habe eine Tabelle mit mehr als 50.000 Datensätzen und möchte Datensätze von 0 bis 10.000 und Datensätze von 10.000 bis 20.000 zurückgeben.

Ich weiß, dass Sie in SQL LIMIT 0,10000am Ende der Abfrage für 0 bis 10.000 und LIMIT 10000,10000am Ende der Abfrage für 10000 bis 20.000 schreiben

Wie wird das in DB2 gemacht? Was ist der Code und die Syntax? (vollständiges Abfragebeispiel wird geschätzt)

elcool
quelle
ROW_NUMBER () wurde nur in iSeries DB2 V5R4 implementiert. Versuchen Sie für frühere Versionen, RRN () zu verwenden, das ähnlich ist.
Paul Morgan
RRN () unterscheidet sich vollständig von row_number ().
Brandon Peterson
hat bei mir nicht funktioniert. Sytanx-Fehler.
Elcool
1
Versuchen Sie es mit RRN (Dateiname), das die physische relative Datensatznummer der Zeile angibt. RRN ist nicht sequentiell und kann Nummern überspringen, wenn Zeilen gelöscht wurden. RRN wird auch nicht nach Schlüssel sequentiell sein, sondern wird basierend auf der Addition sequentiell sein, wenn keine Löschvorgänge aufgetreten sind. In jedem Fall ist RRN für eine Zeile eindeutig und kann zur Auswahl von Teilmengen der Tabelle verwendet werden.
Paul Morgan
1
DB2 bietet Limit-Keyword-Unterstützung von DB2 9.7.2 gemäß Programmierungzen.com/2010/06/02/…
Lakshman

Antworten:

139

Verwenden von FETCH FIRST [n] ROWS ONLY:

http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.perf/db2z_fetchfirstnrows.htm

SELECT LASTNAME, FIRSTNAME, EMPNO, SALARY
  FROM EMP
  ORDER BY SALARY DESC
  FETCH FIRST 20 ROWS ONLY;

Um Bereiche zu erhalten, ROW_NUMBER()müssten Sie (seit v5r4) verwenden und dies innerhalb der WHEREKlausel verwenden: (von hier gestohlen: http://www.justskins.com/forums/db2-select-how-to-123209.html )

SELECT code, name, address
FROM ( 
  SELECT row_number() OVER ( ORDER BY code ) AS rid, code, name, address
  FROM contacts
  WHERE name LIKE '%Bob%' 
  ) AS t
WHERE t.rid BETWEEN 20 AND 25;
Joe
quelle
Ja, das habe ich auch gefunden, hehe. Ich habe die Frage gleichzeitig bearbeitet, um anzuzeigen, dass ich auch mittlere Zeilen möchte.
Elcool
2
Sie müssen so etwas mit ROW_NUMBER tun: justskins.com/forums/db2-select-how-to-123209.html
Joe
ROW_NUMBERist kein gültiges Schlüsselwort. Aber danke für den Link, es gab mir eine Idee und es funktioniert.
Elcool
13

Entwickelte diese Methode:

Sie BRAUCHEN eine Tabelle mit einem eindeutigen Wert, der bestellt werden kann.

Wenn Sie Zeilen zwischen 10.000 und 25.000 möchten und Ihre Tabelle 40.000 Zeilen enthält, müssen Sie zuerst den Startpunkt und die Gesamtzahl der Zeilen ermitteln:

int start = 40000 - 10000;

int total = 25000 - 10000;

Und übergeben Sie diese dann per Code an die Abfrage:

SELECT * FROM 
(SELECT * FROM schema.mytable 
ORDER BY userId DESC fetch first {start} rows only ) AS mini 
ORDER BY mini.userId ASC fetch first {total} rows only
elcool
quelle
Beachten Sie, dass die 10000. Zeile aus der Ergebnismenge ausgeschlossen ist, die erste Zeile die 10001. Zeile.
bläulich
1
Interessante Lösung. Ich wollte es aus Kompatibilitätsgründen mit der H2-Testdatenbank verwenden ... Leider funktioniert es ~ 30-mal langsamer als der SELECT row_number () OVER-Ansatz (ORDER BY code).
Manuna
9

Die Unterstützung für OFFSET und LIMIT wurde kürzlich zu DB2 für i 7.1 und 7.2 hinzugefügt. Sie benötigen die folgenden DB PTF-Gruppenebenen, um diese Unterstützung zu erhalten:

  • SF99702 Level 9 für IBM i 7.2
  • SF99701 Level 38 für IBM i 7.1

Weitere Informationen finden Sie hier: OFFSET- und LIMIT- Dokumentation , DB2 for i Enhancement Wiki

Kevin Adler
quelle
7

Hier ist die Lösung, die ich gefunden habe:

select FIELD from TABLE where FIELD > LASTVAL order by FIELD fetch first N rows only;

Durch Initialisieren von LASTVAL auf 0 (oder '' für ein Textfeld) und anschließendes Setzen auf den letzten Wert in der letzten Gruppe von Datensätzen wird die Tabelle in Blöcken von N Datensätzen durchlaufen.

Tom Barron
quelle
(Ich dachte anfangs, Sie würden den Wert in der Tabelle festlegen, was auf einem gleichzeitigen System spektakulär problematisch wäre. ) Ja, dies sollte in Fällen funktionieren, in denen Sie die Tabelle nacheinander durchlesen, obwohl Sie eine Art von Tabelle benötigen Tie-Breaker-Spalte in dem Fall, in dem sie Nkleiner als die Anzahl identischer Werte in der Spalte ist (obwohl dies auch bei Verwendung zutrifft ROW_NUMBER()). Anfangswerte müssen ebenfalls sorgfältig ausgewählt werden - dies 0ist offensichtlich problematisch, wenn die Spalte einen negativen Wert enthält . Bei Nullen wäre Vorsicht geboten. Funktioniert nicht, wenn Seiten übersprungen werden.
Uhrwerk-Muse
Danke für den Kommentar. Ich denke, es gibt eine implizite Annahme, dass das Feld, das wir zur Steuerung der Abfrage verwenden, eindeutig ist und monoton zunimmt. Ich bin damit einverstanden, dass, wenn diese Annahmen nicht zutreffen, dies nicht funktioniert, um alle Datensätze in der Tabelle zu besuchen. Und natürlich haben Sie Recht, dass Sie mit einem LASTVAL beginnen müssen, das Sinn macht. Im Allgemeinen denke ich, dass Sie mit dem beginnen möchten, was von "select MINIMUM (FIELD) from TABLE" zurückgegeben wird. Wenn das Feld indiziert ist, sind die meisten DB-Engines besser als das sequentielle Lesen der gesamten Tabelle.
Tom Barron
2

Die Lösung von @ elcool ist eine kluge Idee, aber Sie müssen die Gesamtzahl der Zeilen kennen (die sich sogar ändern können, während Sie die Abfrage ausführen!). Also schlage ich eine modifizierte Version vor, die leider 3 Unterabfragen anstelle von 2 benötigt:

select * from (
    select * from (
        select * from MYLIB.MYTABLE
        order by MYID asc 
        fetch first {last} rows only 
        ) I 
    order by MYID desc
    fetch first {length} rows only
    ) II
order by MYID asc

wo {last}sollte durch die Zeilennummer des letzten Datensatzes ersetzt werden, den ich brauche, und {length}sollte durch die Anzahl der Zeilen ersetzt werden, die ich brauche, berechnet als last row - first row + 1.

Wenn ich beispielsweise Zeilen von 10 bis 25 (insgesamt 16 Zeilen) möchte, {last}ist dies 25 und {length}25-10 + 1 = 16.

bläulich
quelle
Ich verachte diejenigen, die abstimmen, wenn sich eine andere Person Zeit nimmt, um ihre Frage zu beantworten.
JP2-Code
1

Sie sollten auch die Klausel OPTIMIZE FOR n ROWS berücksichtigen. Weitere Details hierzu finden Sie in der DB2 LUW-Dokumentation in den Richtlinien zum Einschränken des Themas SELECT-Anweisungen :

  • Die OPTIMIZE FOR-Klausel erklärt die Absicht, nur eine Teilmenge des Ergebnisses abzurufen oder nur die ersten Zeilen abzurufen. Das Optimierungsprogramm kann dann Zugriffspläne auswählen, die die Antwortzeit für das Abrufen der ersten Zeilen minimieren.
David Sky
quelle
1

Versuche dies

SELECT * FROM
    (
        SELECT T.*, ROW_NUMBER() OVER() R FROM TABLE T
    )
    WHERE R BETWEEN 10000 AND 20000
Lucio Menci
quelle
0

Es gibt zwei Lösungen, um eine DB2-Tabelle effizient zu paginieren:

1 - die Technik unter Verwendung der Funktion row_number () und der Klausel OVER, die in einem anderen Beitrag vorgestellt wurde ("SELECT row_number () OVER (ORDER BY ...)"). An einigen großen Tischen bemerkte ich manchmal eine Verschlechterung der Leistung.

2 - die Technik mit einem scrollbaren Cursor. Die Implementierung hängt von der verwendeten Sprache ab. Diese Technik scheint auf großen Tischen robuster zu sein.

Ich habe die beiden in PHP implementierten Techniken während eines Seminars im nächsten Jahr vorgestellt. Die Folie ist unter folgendem Link verfügbar: http://gregphplab.com/serendipity/uploads/slides/DB2_PHP_Best_practices.pdf

Entschuldigung, aber dieses Dokument ist nur in Französisch.

gregphplab
quelle
0

Theres diese verfügbaren Optionen: -

DB2 has several strategies to cope with this problem.
You can use the "scrollable cursor" in feature.
In this case you can open a cursor and, instead of re-issuing a query you can FETCH forward and backward.
This works great if your application can hold state since it doesn't require DB2 to rerun the query every time.
You can use the ROW_NUMBER() OLAP function to number rows and then return the subset you want.
This is ANSI SQL 
You can use the ROWNUM pseudo columns which does the same as ROW_NUMBER() but is suitable if you have Oracle skills.
You can use LIMIT and OFFSET if you are more leaning to a mySQL or PostgreSQL dialect.  
Tyrannisieren
quelle