0Pricing
SQL Academy · Lekcja

PostgreSQL DISTINCT ON

Wybieraj jeden wiersz z każdej grupy

PostgreSQL DISTINCT ON to bezpłatna lekcja SQL Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej SQL Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs SQL Academy zawiera 4 lekcji w sumie.

Czym jest DISTINCT ON?

PostgreSQL oferuje zaawansowane rozszerzenie standardowego słowa kluczowego DISTINCT o nazwie DISTINCT ON. Podczas gdy zwykłe DISTINCT usuwa całkowicie zduplikowane wiersze, DISTINCT ON pozwala wybrać dokładnie jeden wiersz na grupę na podstawie jednej lub kilku wybranych kolumn.

Można myśleć o tym tak: „Dla każdej unikalnej wartości w tej kolumnie zwróć mi jeden wiersz”. Jest to niezwykle przydatne, gdy chcą Państwo znaleźć najnowsze zamówienie każdego klienta, najwyższy wynik każdego ucznia lub pierwsze zdarzenie w każdej kategorii.

Podstawowa składnia DISTINCT ON

Składnia umieszcza DISTINCT ON (column) bezpośrednio za SELECT. Kolumna w nawiasach definiuje grupowanie — PostgreSQL zwróci jeden wiersz dla każdej unikalnej wartości tej kolumny.

Poniższy przykład zwraca jeden wiersz dla każdego customer_id z tabeli zamówień. PostgreSQL wybiera zwracany wiersz na podstawie następującej po nim klauzuli ORDER BY.

SELECT DISTINCT ON (customer_id)
  customer_id,
  order_id,
  order_date,
  total_amount
FROM orders
ORDER BY customer_id, order_date DESC;

Przygotowanie przykładowych tabel

Utwórzmy prostą tabelę orders i wstawmy do niej przykładowe wiersze, aby zobaczyć DISTINCT ON w działaniu. Mamy trzech klientów, z których każdy złożył wiele zamówień w różnych dniach.

CREATE TABLE orders (
  order_id      SERIAL PRIMARY KEY,
  customer_id   INT,
  order_date    DATE,
  total_amount  NUMERIC(10, 2)
);

INSERT INTO orders (customer_id, order_date, total_amount) VALUES
  (1, '2024-01-05', 120.00),
  (1, '2024-03-12', 85.50),
  (1, '2024-06-20', 200.00),
  (2, '2024-02-14', 45.00),
  (2, '2024-05-30', 310.00),
  (3, '2024-04-01', 75.00);

Najnowsze zamówienie klienta

Bardzo częsty przypadek użycia: znalezienie najnowszego zamówienia każdego klienta. Sortując order_date DESC w obrębie każdej grupy customer_id, DISTINCT ON wybiera wiersz z najnowszą datą.

Proszę zauważyć, że klauzula ORDER BY musi zaczynać się od tej samej kolumny lub tych samych kolumn, które wymieniono w DISTINCT ON. Jest to wymaganie PostgreSQL.

SELECT DISTINCT ON (customer_id)
  customer_id,
  order_id,
  order_date,
  total_amount
FROM orders
ORDER BY customer_id, order_date DESC;

Najwcześniejsze zamówienie klienta

Aby zamiast tego uzyskać pierwsze (najstarsze) zamówienie każdego klienta, wystarczy zmienić kierunek sortowania na ASC. Jedyną różnicą jest kolejność wierszy w obrębie każdej grupy — DISTINCT ON zawsze wybiera pierwszy wiersz po sortowaniu.

SELECT DISTINCT ON (customer_id)
  customer_id,
  order_id,
  order_date,
  total_amount
FROM orders
ORDER BY customer_id, order_date ASC;

Reguła ORDER BY

Ważna reguła: Przy użyciu DISTINCT ON (col) klauzula ORDER BY musi zaczynać się od tej samej kolumny lub tych samych kolumn, które wymieniono wewnątrz DISTINCT ON. Jeśli tak nie jest, PostgreSQL zgłosi błąd.

Po kolumnie lub kolumnach grupujących można dodać dowolne dodatkowe kryteria sortowania, aby określić, który wiersz z danej grupy zostanie wybrany.

-- Correct: ORDER BY starts with the DISTINCT ON column
SELECT DISTINCT ON (customer_id)
  customer_id, order_date, total_amount
FROM orders
ORDER BY customer_id, total_amount DESC;

-- This would cause an error:
-- ORDER BY order_date DESC  (missing customer_id at the start)

Najwyższy wynik ucznia

Oto kolejny praktyczny przykład z użyciem tabeli test_scores. Chcemy znaleźć najwyższy wynik, jaki kiedykolwiek uzyskał każdy uczeń. Sortując score DESC w obrębie grupy każdego ucznia, DISTINCT ON zwraca tylko wiersz z najwyższym wynikiem dla danego ucznia.

CREATE TABLE test_scores (
  id         SERIAL PRIMARY KEY,
  student_id INT,
  subject    VARCHAR(50),
  score      INT,
  taken_on   DATE
);

INSERT INTO test_scores (student_id, subject, score, taken_on) VALUES
  (101, 'Math',    92, '2024-02-10'),
  (101, 'Math',    78, '2024-04-15'),
  (102, 'Math',    85, '2024-02-10'),
  (102, 'Math',    91, '2024-04-15'),
  (103, 'Math',    67, '2024-02-10');

SELECT DISTINCT ON (student_id)
  student_id, subject, score, taken_on
FROM test_scores
ORDER BY student_id, score DESC;

DISTINCT ON z wieloma kolumnami

Można grupować według więcej niż jednej kolumny, wymieniając wiele kolumn wewnątrz DISTINCT ON. Zwraca to jeden wiersz dla każdej unikalnej kombinacji tych kolumn.

Poniższy przykład wybiera najwyższy wynik każdego ucznia z każdego przedmiotu, traktując każdą parę (uczeń, przedmiot) jako osobną grupę.

INSERT INTO test_scores (student_id, subject, score, taken_on) VALUES
  (101, 'Science', 88, '2024-03-01'),
  (101, 'Science', 95, '2024-05-20'),
  (102, 'Science', 72, '2024-03-01');

SELECT DISTINCT ON (student_id, subject)
  student_id, subject, score, taken_on
FROM test_scores
ORDER BY student_id, subject, score DESC;

Filtrowanie za pomocą WHERE

DISTINCT ON działa naturalnie w połączeniu z klauzulami WHERE. Najpierw stosowany jest filtr, a następnie DISTINCT ON wybiera jeden wiersz z każdej grupy wśród przefiltrowanych wyników.

W tym przykładzie znajdujemy najnowsze zamówienie każdego klienta, ale tylko wśród zamówień o wartości większej niż 100.

SELECT DISTINCT ON (customer_id)
  customer_id,
  order_id,
  order_date,
  total_amount
FROM orders
WHERE total_amount > 100
ORDER BY customer_id, order_date DESC;

DISTINCT ON a GROUP BY

Zarówno DISTINCT ON, jak i GROUP BY mogą zwrócić jeden wiersz na grupę, ale służą do różnych celów:

  • GROUP BY scala wiersze i wymaga funkcji agregujących (SUM, MAX itp.) dla kolumn, które nie służą do grupowania.
  • DISTINCT ON zachowuje rzeczywiście istniejący wiersz — wszystkie jego kolumny są dostępne bez agregacji.

Proszę używać GROUP BY, gdy potrzebują Państwo zagregowanych wartości. Proszę używać DISTINCT ON, gdy potrzebują Państwo pełnych danych z konkretnego wiersza w każdej grupie.

-- GROUP BY: only aggregated columns allowed
SELECT customer_id, MAX(order_date) AS latest_date
FROM orders
GROUP BY customer_id;

-- DISTINCT ON: returns the whole row for that latest date
SELECT DISTINCT ON (customer_id)
  customer_id, order_id, order_date, total_amount
FROM orders
ORDER BY customer_id, order_date DESC;

Używanie DISTINCT ON w podzapytaniu

Czasami trzeba dodatkowo filtrować lub sortować wynik DISTINCT ON. Ponieważ zewnętrzna klauzula ORDER BY jest powiązana z kolumną grupującą, można umieścić zapytanie w podzapytaniu (lub CTE), aby zastosować inne sortowanie do wyniku końcowego.

W tym przykładzie najpierw wybieramy najnowsze zamówienie każdego klienta, a następnie sortujemy wynik końcowy malejąco według total_amount.

SELECT *
FROM (
  SELECT DISTINCT ON (customer_id)
    customer_id,
    order_id,
    order_date,
    total_amount
  FROM orders
  ORDER BY customer_id, order_date DESC
) AS latest_orders
ORDER BY total_amount DESC;

Szybkie sprawdzenie

Sprawdźmy, czy rozumieją Państwo działanie DISTINCT ON. Proszę uważnie przeczytać poniższe zapytanie i wybrać odpowiedź, która najlepiej opisuje zwracany wynik.

SELECT DISTINCT ON (department_id) department_id, employee_name, salary FROM employees ORDER BY department_id, salary DESC;

Podsumowanie lekcji

Świetna praca! Oto podsumowanie tego, czego nauczyli się Państwo o PostgreSQL DISTINCT ON:

  • DISTINCT ON (col) zwraca dokładnie jeden wiersz dla każdej unikalnej wartości określonej kolumny lub określonych kolumn.
  • Klauzula ORDER BY musi zaczynać się od tej samej kolumny lub tych samych kolumn, które wymieniono w DISTINCT ON — decyduje ona o tym, który wiersz zostanie wybrany z każdej grupy.
  • Można używać wielu kolumn: DISTINCT ON (col1, col2) grupuje według kombinacji obu kolumn.
  • W przeciwieństwie do GROUP BY, DISTINCT ON zwraca rzeczywisty wiersz ze wszystkimi jego oryginalnymi kolumnami — agregacja nie jest potrzebna.
  • Zapytanie należy umieścić w podzapytaniu, gdy trzeba posortować wynik końcowy według innej kolumny.

DISTINCT ON to funkcja charakterystyczna dla PostgreSQL i jeden z najbardziej eleganckich sposobów rozwiązywania problemów typu „wybierz jeden wiersz na grupę”.

Często zadawane pytania

Czy lekcja „PostgreSQL DISTINCT ON” jest bezpłatna?

Tak — pełny tekst „PostgreSQL DISTINCT ON” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu SQL Academy, przejdź na CoddyKit PRO. Kurs SQL Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „PostgreSQL DISTINCT ON”?

Wybieraj jeden wiersz z każdej grupy Ćwiczysz SQL Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć SQL Academy?

Nie wymagamy żadnego doświadczenia. SQL Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „PostgreSQL DISTINCT ON”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji SQL Academy?

Tak. Każda lekcja SQL Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Podstawy SELECT DISTINCT
  2. DISTINCT dla wielu kolumn
  3. PostgreSQL DISTINCT ON
  4. Zliczanie unikatowych wartości
← Powrót do SQL Academy