Odpowiednik LIMIT dla DB2

93

Jak to robisz LIMITw DB2 for iSeries?

Mam tabelę zawierającą ponad 50 000 rekordów i chcę zwrócić rekordy od 0 do 10 000 i rekordy od 10 000 do 20 000.

Wiem, że w SQL piszesz LIMIT 0,10000na końcu zapytania od 0 do 10000 a LIMIT 10000,10000na końcu zapytania od 10000 do 20 000

Jak więc to się robi w DB2? Jaki jest kod i składnia? (przykład pełnego zapytania jest mile widziany)

elcool
źródło
ROW_NUMBER () została zaimplementowana tylko w iSeries DB2 V5R4. W poprzednich wersjach spróbuj użyć RRN (), która jest podobna.
Paul Morgan,
RRN () jest zupełnie inny niż row_number ().
Brandon Peterson,
nie działa dla mnie. Błąd Sytanx.
elcool
1
Spróbuj RRN (nazwa pliku), która poda fizyczny względny numer rekordu wiersza. RRN nie będzie sekwencyjne i może pomijać liczby, jeśli usunięto wiersze. RRN również nie będzie sekwencyjne według klucza, ale będzie sekwencyjne na podstawie dodawania, jeśli nie wystąpiły żadne usunięcia. W każdym przypadku RRN będzie unikalne dla wiersza i może służyć do wybierania podzbiorów tabeli.
Paul Morgan,
1
DB2 zapewnia obsługę słów kluczowych limit z DB2 9.7.2 zgodnie z programowaniemzen.com/2010/06/02/ ...
lakshman

Odpowiedzi:

141

Używając 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;

Aby uzyskać zakresy, musisz użyć ROW_NUMBER()(od wersji v5r4) i użyć tego w WHEREklauzuli: (skradzione stąd: 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
źródło
tak, też to znalazłem, hehe. W tym samym czasie edytowałem pytanie, aby wskazać, że chcę również środkowych wierszy.
elcool
2
Musisz zrobić coś takiego z ROW_NUMBER: justskins.com/forums/db2-select-how-to-123209.html
Joe
ROW_NUMBERnie jest prawidłowym słowem kluczowym. Ale dzięki za link, dał mi pomysł i działa.
elcool
13

Opracował tę metodę:

POTRZEBUJESZ tabeli, która ma unikalną wartość, którą można zamówić.

Jeśli chcesz uzyskać wiersze od 10 000 do 25 000, a Twoja tabela ma 40 000 wierszy, najpierw musisz uzyskać punkt początkowy i całkowitą liczbę wierszy:

int start = 40000 - 10000;

int total = 25000 - 10000;

A następnie przekaż je kodem do zapytania:

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
źródło
Zwróć uwagę, że 10000-ty wiersz jest wykluczony z zestawu wyników, pierwszy wiersz to 10001.
niebieskawe
1
Ciekawe rozwiązanie. Miałem zamiar użyć go do zapewnienia zgodności z testową bazą danych H2 ... Niestety, działa on ~ 30 razy wolniej niż metoda SELECT row_number () OVER (ORDER BY code).
manuna,
9

Wsparcie dla OFFSET i LIMIT zostało ostatnio dodane do DB2 for i 7.1 i 7.2. Aby uzyskać tę obsługę, potrzebne są następujące poziomy grup DB PTF:

  • SF99702 poziom 9 dla IBM i 7.2
  • SF99701 poziom 38 dla IBM i 7.1

Więcej informacji można znaleźć tutaj: dokumentacja OFFSET i LIMIT , DB2 for i Enhancement Wiki

Kevin Adler
źródło
7

Oto rozwiązanie, które wymyśliłem:

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

Poprzez zainicjowanie LASTVAL na 0 (lub „” dla pola tekstowego), a następnie ustawienie ostatniej wartości w najnowszym zestawie rekordów, spowoduje to przejście przez tabelę w fragmentach N rekordów.

Tom Barron
źródło
(Początkowo myślałem, że ustawiasz wartość w tabeli, co byłoby spektakularnie problematyczne w systemie współbieżnym) Tak, powinno to zadziałać w przypadkach, gdy wykonujesz sekwencyjne czytanie tabeli, chociaż potrzebujesz jakiegoś rodzaju kolumna rozstrzygająca w przypadku, gdy Njest mniejsza niż liczba identycznych wartości w kolumnie (chociaż jest to prawdą również w przypadku użycia ROW_NUMBER()). Wartości początkowe również należy dobierać ostrożnie - 0oczywiście będzie to problematyczne, jeśli kolumna zawiera wartość ujemną . Konieczna będzie ostrożność w przypadku wartości zerowych. Nie będzie działać, jeśli strony zostaną pominięte.
Clockwork-Muse
Dziękuję za komentarz. Myślę, że istnieje niejawne założenie, że pole, którego używamy do kontrolowania zapytania, jest unikalne i monotonicznie rosnące. Zgadzam się, że jeśli te założenia się nie sprawdzą, przeglądanie wszystkich rekordów w tabeli nie zadziała. I oczywiście masz rację, że musiałbyś zacząć od LASTVAL, który ma sens. Ogólnie myślę, że chciałbyś zacząć od tego, co jest zwracane przez „wybierz MINIMUM (POLE) z TABELI”. Jeśli pole jest indeksowane, większość silników db poradzi sobie lepiej niż sekwencyjne czytanie całej tabeli.
Tom Barron
2

Rozwiązanie @ elcool to sprytny pomysł, ale musisz znać całkowitą liczbę wierszy (która może nawet ulec zmianie podczas wykonywania zapytania!). Proponuję więc zmodyfikowaną wersję, która niestety wymaga 3 podzapytań zamiast 2:

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

gdzie {last}powinien zostać zastąpiony numerem wiersza ostatniego potrzebnego rekordu, a {length}liczbą potrzebnych wierszy, obliczoną jakolast row - first row + 1 .

Np. Jeśli chcę rzędy od 10 do 25 (łącznie 16 rzędów), {last}będzie 25 i {length}będzie 25-10 + 1 = 16.

niebieskawy
źródło
Gardzę tymi, którzy głosują przeciw, gdy inna osoba potrzebuje czasu, aby odpowiedzieć na ich pytanie.
jp2code
1

Należy również wziąć pod uwagę klauzulę OPTIMIZE FOR n ROWS. Więcej szczegółów na ten temat w dokumentacji DB2 LUW w temacie Wytyczne dotyczące ograniczania instrukcji SELECT :

  • Klauzula OPTIMIZE FOR deklaruje zamiar pobrania tylko podzbioru wyniku lub nadanie priorytetu pobieraniu tylko kilku pierwszych wierszy. Optymalizator może następnie wybrać plany dostępu, które minimalizują czas odpowiedzi na pobranie pierwszych kilku wierszy.
David Sky
źródło
1

Spróbuj tego

SELECT * FROM
    (
        SELECT T.*, ROW_NUMBER() OVER() R FROM TABLE T
    )
    WHERE R BETWEEN 10000 AND 20000
Lucio Menci
źródło
0

Istnieją 2 sposoby wydajnego dzielenia stron na strony w tabeli DB2:

1 - technika wykorzystująca funkcję row_number () i klauzulę OVER, która została zaprezentowana w innym poście ("SELECT row_number () OVER (ORDER BY ...)"). Na niektórych dużych stołach zauważyłem czasami degradację występów.

2 - technika wykorzystująca przewijany kursor. Implementacja zależy od używanego języka. Ta technika wydaje się solidniejsza na dużych stołach.

Przedstawiłem 2 techniki wdrożone w PHP na seminarium w przyszłym roku. Slajd jest dostępny pod tym linkiem: http://gregphplab.com/serendipity/uploads/slides/DB2_PHP_Best_practices.pdf

Przepraszamy, ale ten dokument jest tylko w języku francuskim.

gregphplab
źródło
0

Tam są dostępne opcje: -

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.  
Zabijaka
źródło