PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Wybrać pierwszy wiersz w każdej grupie GROUP BY?

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 lub ORDER 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ź.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak utworzyć pojedynczy punkt końcowy dla konfiguracji replikacji PostgreSQL za pomocą HAProxy?

  2. Postgres:Dodaj ograniczenie, jeśli jeszcze nie istnieje

  3. Jak przekazać hasło do pg_dump?

  4. Dodanie kolumny jako klucza obcego powoduje, że kolumna ERROR, do której odwołuje się ograniczenie klucza obcego, nie istnieje

  5. [Wideo] Wprowadzenie do typów danych JSON w PostgreSQL