SQL od podstaw · część 13

ORDER BY, LIMIT, TOP i FETCH

Ustal kolejność, wybierz pierwsze wiersze i obsłuż remisy. Porównaj ograniczanie wyników w czterech dialektach.

Część bazodanowa INF.03

Bez ORDER BY kolejność nie jest obietnicą

Tabela nie jest arkuszem z trwałą kolejnością wierszy. Jeśli raport ma zaczynać się od najdroższego produktu, zapisz to w zapytaniu. ASC oznacza rosnąco i jest domyślne; DESC oznacza malejąco.

SELECT id, nazwa, cena FROM produkty
ORDER BY cena DESC, id ASC;

Wynik po resecie: Monitor 800, Klawiatura 120, Mysz 60, Kabel 25. Najpierw decyduje cena. Gdy ceny są równe, mniejsze id jest wcześniej. Kierunek dotyczy każdej kolumny osobno: ORDER BY cena, id DESC nie sortuje obu kolumn malejąco.

Dwa najdroższe produkty — cztery sposoby

W każdym przykładzie sortowanie rozstrzyga remisy przez unikatowe id. Dzięki temu prosimy o ten sam jednoznaczny zestaw. Sam limit bez ORDER BY nie oznacza „najdroższe”.

MySQL / MariaDB

SELECT id, nazwa, cena
FROM produkty
ORDER BY cena DESC, id
LIMIT 2;

SQL Server

SELECT TOP (2) id, nazwa, cena
FROM produkty
ORDER BY cena DESC, id;

Oracle 19c

SELECT id, nazwa, cena
FROM produkty
ORDER BY cena DESC, id
FETCH FIRST 2 ROWS ONLY;

Access / ACE

SELECT TOP 2 id, nazwa, cena
FROM produkty
ORDER BY cena DESC, id;

Wynik: Monitor i Klawiatura. LIMIT stoi na końcu, TOP przy SELECT, a FETCH po ORDER BY. Składnia Oracle FETCH jest dostępna od 12c; nie przenoś jej bez sprawdzenia do starszego serwera. Natywny SQLite w edytorze używa LIMIT. Aby przećwiczyć TOP (2), wybierz wariant SQL Server — edytor pokaże mapowanie na SQLite LIMIT.

Wypróbuj zapytanie

SQLite — rzeczywisty silnik przeglądarki. MySQL / SQL Server — ograniczona emulacja składni. Wybierz tryb SQLite do natywnego LIKE. Pozostałe tryby nie są rzeczywistymi serwerami. Ścisłe typy, auto-ID i administrację ćwicz w prawdziwym SZBD.

Ładowanie edytora…

Remisy: „dokładnie dwa” czy „wszyscy ex aequo”?

SQL Server TOP (2) bez WITH TIES ogranicza liczbę do dwóch wierszy, ale przy niejednoznacznym ORDER BY nie określa, które remisujące wiersze wybierze. TOP (2) WITH TIES może zwrócić więcej, aby uwzględnić remis na granicy. Oracle ma podobną opcję FETCH FIRST … WITH TIES.

Access TOP nie rozstrzyga równych wartości sortowania i może zwrócić więcej niż n wierszy. Jeżeli w ORDER BY podasz także unikatowy klucz, nie będzie remisu całego klucza sortowania. MySQL LIMIT nie ma w tym podstawowym zapisie opcji WITH TIES. Edytor nie emuluje WITH TIES ani Access TOP — użyj rzeczywistego silnika do porównania tych reguł.

Stronicowanie

Przy stronie wielkości 2 pierwszy zestaw ma przesunięcie 0, drugi 2, trzeci 4. OFFSET oznacza liczbę pominiętych wierszy, nie numer strony. Dla numeracji stron od 1: przesunięcie = (numer strony − 1) × rozmiar.

-- MySQL / MariaDB (oraz natywny SQLite)
SELECT id, nazwa, cena FROM produkty
ORDER BY cena DESC, id
LIMIT 2 OFFSET 2;

-- SQL Server 2012+ oraz Oracle 19c
SELECT id, nazwa, cena FROM produkty
ORDER BY cena DESC, id
OFFSET 2 ROWS FETCH NEXT 2 ROWS ONLY;

Druga strona zawiera Mysz i Kabel. Przykłady stronicowania z OFFSET uruchom we właściwym silniku lub zewnętrznym kliencie SQLite. Obecna wersja edytora w kursie ogranicza ten fragment do zwykłego LIMIT i TOP; OFFSET/FETCH odrzuca jawnie. Access nie ma bezpośredniego odpowiednika tej składni; dla małych raportów można użyć kwerend z TOP, a w aplikacji świadomie zaprojektować stronicowanie.

Jeżeli między pobraniami stron ktoś zmieni dane, same OFFSET i ORDER BY nie zapewniają niezmiennej migawki. W dużych zbiorach głębokie przesunięcia też bywają kosztowne. To powód, aby później poznać transakcje i stronicowanie według ostatniego klucza, a nie zwiększać bez końca OFFSET.

NULL, napisy i aliasy

MySQL i SQL Server zwykle umieszczają NULL przed wartościami przy ASC, Oracle domyślnie po nich. Kolacja określa porządek tekstu. Jeśli raport wymaga braków na końcu, opisz regułę jawnie, np. w MySQL, SQL Server, Oracle i SQLite:

SELECT imie, miasto FROM klienci
ORDER BY CASE WHEN miasto IS NULL THEN 1 ELSE 0 END,
         miasto, id;

W kwerendzie Access użyj odpowiednika IIf(miasto Is Null, 1, 0) zamiast CASE. W Oracle można też użyć ORDER BY miasto NULLS LAST. Nie zakładaj, że NULL jest pustym napisem.

Alias z SELECT może zwykle służyć do sortowania, np. SELECT cena * 0.9 AS po_rabacie FROM produkty ORDER BY po_rabacie;. Dla czytelności preferuj nazwę zamiast pozycji ORDER BY 1, która po zmianie listy SELECT może oznaczać inną kolumnę.

Ćwiczenia

  1. Wybierz najtańszy produkt w MySQL i SQL Server.
  2. Wyświetl trzech pierwszych klientów po id malejąco w SQLite.
  3. Wyjaśnij, dlaczego TOP 2 w Access może zwrócić trzy osoby.
Rozwiązania
-- MySQL / SQLite
SELECT nazwa, cena FROM produkty ORDER BY cena ASC, id LIMIT 1;
-- SQL Server
SELECT TOP (1) nazwa, cena FROM produkty ORDER BY cena ASC, id;
-- SQLite
SELECT id, imie FROM klienci ORDER BY id DESC LIMIT 3;

1. Kabel, 25. 2. Adam (5), Ewa (4), O’Neil (3). 3. Remis wartości sortowania na granicy TOP. Dodaj unikatowy klucz, jeśli potrzebujesz jednoznacznego wyboru dokładnie dwóch wierszy.

Źródła: MySQL SELECT/LIMIT, SQL Server TOP, Oracle SELECT/FETCH, Access TOP. INF.03.4: porządkowanie i ograniczanie wyników. Dalej: funkcje skalarne tekstowe.

Przewijanie do góry