DISTINCT ON
jest zazwyczaj najprostszy i najszybszy do tego w PostgreSQL .
(Optymalizacja wydajności dla niektórych obciążeń, patrz poniżej.)
SELECT DISTINCT ON (customer)
id, customer, total
FROM purchases
ORDER BY customer, total DESC, id;
Lub krótsze (jeśli nie tak jasne) z numerami porządkowymi kolumn wyjściowych:
SELECT DISTINCT ON (2)
id, customer, total
FROM purchases
ORDER BY 2, 3 DESC, 1;
Jeśli total
może być NULL (nie zaszkodzi w żaden sposób, ale będziesz chciał dopasować istniejące indeksy):
...
ORDER BY customer, total DESC NULLS LAST, id;
Główne punkty
DISTINCT ON
jest rozszerzeniem standardu PostgreSQL (gdzie tylko DISTINCT
na całym SELECT
lista jest zdefiniowana).
Wymień dowolną liczbę wyrażeń w DISTINCT ON
klauzula, połączona wartość wiersza definiuje duplikaty. Instrukcja:
Oczywiście dwa wiersze są uważane za różne, jeśli różnią się co najmniej jedną wartością kolumny. W tym porównaniu wartości null są uważane za równe.
Pogrubiony nacisk na moje.
DISTINCT ON
można łączyć z ORDER BY
. Wyrażenia wiodące w ORDER BY
musi znajdować się w zestawie wyrażeń w DISTINCT ON
, ale możesz dowolnie zmieniać ich kolejność. Przykład.
Możesz dodać dodatkowe wyrażenia do ORDER BY
wybrać konkretny wiersz z każdej grupy rówieśników. Lub, jak mówi instrukcja:
DISTINCT ON
wyrażenie(a) musi pasować do skrajnego lewego ORDER BY
wyrażenie(a) ORDER BY
Klauzula zwykle zawiera dodatkowe wyrażenia, które określają pożądany priorytet wierszy w każdym DISTINCT ON
grupa.
Dodałem id
jako ostatni element do zerwania więzi:
"Wybierz wiersz z najmniejszym id
z każdej grupy dzielącej najwyższą total
”.
Aby uporządkować wyniki w sposób, który nie zgadza się z porządkiem sortowania określającym pierwszy na grupę, możesz zagnieździć powyższe zapytanie w zewnętrznym zapytaniu z innym ORDER BY
. Przykład.
Jeśli total
może być NULL, najprawdopodobniej chcesz wiersz z największą wartością inną niż null. Dodaj NULLS LAST
jak zademonstrowano. Zobacz:
- Sortować według kolumny ASC, ale najpierw wartości NULL?
SELECT
lista nie jest ograniczony wyrażeniami w DISTINCT ON
lub ORDER BY
w jakikolwiek sposób. (Nie potrzebne w prostym przypadku powyżej):
-
nie musisz dołącz dowolne z wyrażeń w
DISTINCT ON
lubORDER BY
. -
możesz uwzględnij dowolne inne wyrażenie w
SELECT
lista. Jest to kluczowe dla zastąpienia znacznie bardziej złożonych zapytań podzapytaniami i funkcjami agregacji/okna.
Testowałem z Postgresem w wersjach 8.3 – 13. Ale ta funkcja była tam przynajmniej od wersji 7.1, więc w zasadzie zawsze.
Indeks
idealny indeks dla powyższego zapytania byłby indeksem wielokolumnowym obejmującym wszystkie trzy kolumny w pasującej kolejności i z pasującą kolejnością sortowania:
CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);
Może być zbyt wyspecjalizowany. Ale użyj go, jeśli wydajność odczytu dla konkretnego zapytania ma kluczowe znaczenie. Jeśli masz DESC NULLS LAST
w zapytaniu użyj tego samego w indeksie, aby kolejność sortowania była zgodna i indeks miał zastosowanie.
Skuteczność / Optymalizacja wydajności
Rozważ koszty i korzyści przed utworzeniem dostosowanych indeksów dla każdego zapytania. Potencjał powyższego indeksu w dużej mierze zależy od dystrybucji danych .
Indeks jest używany, ponieważ dostarcza wstępnie posortowane dane. W Postgresie 9.2 lub nowszym zapytanie może również skorzystać z skanowania tylko indeksu jeśli indeks jest mniejszy niż tabela bazowa. Indeks musi być jednak zeskanowany w całości.
Dla niewielu wierszy na klienta (wysoka kardynalność w kolumnie customer
), jest to bardzo wydajne. Tym bardziej, jeśli i tak potrzebujesz posortowanych danych wyjściowych. Korzyści maleją wraz ze wzrostem liczby wierszy na klienta.
Idealnie, masz wystarczająco dużo work_mem
przetworzyć zaangażowany krok sortowania w pamięci RAM i nie rozlać na dysk. Ale ogólnie ustawienie work_mem
też wysoki może mieć negatywne skutki. Rozważ SET LOCAL
dla wyjątkowo dużych zapytań. Dowiedz się, ile potrzebujesz, korzystając z EXPLAIN ANALYZE
. Wzmianka o „Dysk: " w kroku sortowania wskazuje, że potrzeba więcej:
- Parametr konfiguracyjny work_mem w PostgreSQL na Linuksie
- Zoptymalizuj proste zapytanie, używając daty i tekstu ORDER BY
Dla wielu wierszy na klienta (niska kardynalność w kolumnie customer
), luźne skanowanie indeksu (znane również jako „pomiń skanowanie”) byłoby (znacznie) wydajniejsze, ale nie zostało to zaimplementowane do wersji Postgres 14. (Implementacja skanowania tylko do indeksu jest opracowywana dla Postgres 15. Zobacz tutaj i tutaj.)
Dla teraz istnieją szybsze techniki zapytań zastąpić to. W szczególności, jeśli masz osobny stół z unikalnymi klientami, co jest typowym przypadkiem użycia. Ale także, jeśli tego nie zrobisz:
- SELECT DISTINCT jest wolniejszy niż oczekiwano na moim stole w PostgreSQL
- Zoptymalizuj zapytanie GROUP BY, aby pobrać ostatni wiersz na użytkownika
- Optymalizuj maksymalne zapytanie grupowe
- Zapytaj ostatnie N powiązanych wierszy na wiersz
Wzorce
Zobacz oddzielną odpowiedź.