Dlaczego nie używać ROWNUM do limitowania danych?

Odpowiedź jest bardzo prosta – rownum z limitowaniem danych w sposób bezpośredni sobie nie radzi.

1. Tworzymy przykładową tabelę

create table test_rownum (test_date date)
/
insert into test_rownum values (to_date('01/01/2000', 'dd-mm-yyyy'));
insert into test_rownum values (to_date('02/01/2000', 'dd-mm-yyyy'));
insert into test_rownum values (to_date('03/01/2000', 'dd-mm-yyyy'));
insert into test_rownum values (to_date('04/01/2000', 'dd-mm-yyyy'));
insert into test_rownum values (to_date('05/01/2000', 'dd-mm-yyyy'));
insert into test_rownum values (to_date('12/01/2000', 'dd-mm-yyyy'));
insert into test_rownum values (to_date('24/01/2000', 'dd-mm-yyyy'));
insert into test_rownum values (to_date('15/01/2000', 'dd-mm-yyyy'));

2. Wynik zapytania:

select rownum, test_date from test_rownum order by test_date

3. Następnie odczuwamy potrzebę limitowania danych poprzez rownum, czyli chcemy tylko 7 pierwszych rekordów:

select rownum, test_date from test_rownum
where rownum<=7
order by test_date

4. W wyniku otrzymujemy

5. Brakuje jednego rekordu – dość istotnego z punktu widzenia sortowania

6. Jak widać rownum powoduje „drobne zamieszanie”. Dlaczegóż? Poniewóż ROWNUM numeruje kolejne odczytane wiersze co widać w pierwszym wyniku i zamienionych wartościach 7 i 8 w kolumnie ROWNUM. Sortowanie po ROWNUM da identyczny wynik, bo dane fizycznie w tabeli są zapisane w sposób nieuporządkowany(w zależności od momentu wstawienia ich do tabeli). Rozwiązanie? Podzapytanie:

select rownum, t1.test_date from (
    select test_date from test_rownum
    order by test_date ) t1
where rownum<=7

7. Reasumując
a. Lepiej nie używać ROWNUM jako limitera posortowanych danych
b. Jeśli już taka kolejność zachodzi, koniecznie trzeba użyć podzapytania
c. Nie korzystać z ROWNUM szukając konkretnej pozycji, np. ROWNUM=3, gdyż otrzymamy pusty wynik (rownum pobiera kolejny numer dla konkretnego zapytania, zatem w takim wypadku wartości 3 nie znajdzie – znów trzeba korzystać z podzapytania)
d. Jako alternatywy sugeruję używać FETCH(od wersji ORACLE 12c) – polecam opis z dodatkowymi możliwościami:
https://community.oracle.com/docs/DOC-995907

select rownum, test_date from  test_rownum
order by test_date
fetch first 7 rows only

e. I jak zobaczycie ROWNUM to możecie wietrzyć kłopoty z jakością danych

Written by Przemysław Starosta