sql >> Database teknologi >  >> RDS >> PostgreSQL

Vælg første række i hver GRUPPE FOR gruppe?

DISTINCER TIL er typisk enklest og hurtigst til dette i PostgreSQL .
(For ydeevneoptimering for visse arbejdsbelastninger se nedenfor.)

SELECT DISTINCT ON (customer)
       id, customer, total
FROM   purchases
ORDER  BY customer, total DESC, id;

Eller kortere (hvis ikke så tydeligt) med ordenstal af outputkolonner:

SELECT DISTINCT ON (2)
       id, customer, total
FROM   purchases
ORDER  BY 2, 3 DESC, 1;

Hvis total kan være NULL (vil ikke skade på nogen måde, men du vil gerne matche eksisterende indekser):

...
ORDER  BY customer, total DESC NULLS LAST, id;

Vigtige punkter

DISTINCER TIL er en PostgreSQL-udvidelse af standarden (hvor kun DISTINCT i det hele taget SELECT listen er defineret).

Angiv et vilkårligt antal udtryk i DISTINCT ON klausul, definerer den kombinerede rækkeværdi dubletter. Manualen:

Det er klart, at to rækker betragtes som adskilte, hvis de adskiller sig i mindst én kolonneværdi. Nul-værdier betragtes som ens i denne sammenligning.

Fed fremhævelse mine.

DISTINCER TIL kan kombineres med ORDER BY . Førende udtryk i ORDER BY skal være i sættet af udtryk i DISTINCT ON , men du kan frit omarrangere rækkefølgen blandt dem. Eksempel.
Du kan tilføje yderligere udtryk til ORDER BY at vælge en bestemt række fra hver gruppe af jævnaldrende. Eller, som manualen udtrykker det:

DISTINCT ON udtryk skal matche ORDER BY længst til venstre udtryk). ORDER BY klausul vil normalt indeholde yderligere udtryk, der bestemmer den ønskede forrang for rækker inden for hver DISTINCT ON gruppe.

Jeg tilføjede id som sidste element til at bryde båndene:
"Vælg rækken med det mindste id fra hver gruppe, der deler det højeste total ."

For at sortere resultater på en måde, der ikke stemmer overens med sorteringsrækkefølgen, der bestemmer den første pr. gruppe, kan du indlejre over forespørgslen i en ydre forespørgsel med en anden ORDER BY . Eksempel.

Hvis total kan være NULL, du sandsynligvis ønsker rækken med den største ikke-nul værdi. Tilføj NULLS LAST som vist. Se:

  • Sortere efter kolonne ASC, men NULL-værdier først?

SELECT liste er ikke begrænset af udtryk i DISTINCT ON eller BEstil efter på nogen måde. (Ikke nødvendigt i det simple tilfælde ovenfor):

  • Du behøver ikke inkludere et hvilket som helst af udtrykkene i DISTINCT ON eller BEstil efter .

  • Du kan inkludere ethvert andet udtryk i SELECT liste. Dette er medvirkende til at erstatte meget mere komplekse forespørgsler med underforespørgsler og aggregat-/vinduefunktioner.

Jeg testede med Postgres version 8.3 – 13. Men funktionen har været der i hvert fald siden version 7.1, så stort set altid.

Indeks

Det perfekte indeks for ovenstående forespørgsel ville være et indeks med flere kolonner, der spænder over alle tre kolonner i matchende rækkefølge og med matchende sorteringsrækkefølge:

CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);

Kan være for specialiseret. Men brug det, hvis læseydelsen for den bestemte forespørgsel er afgørende. Hvis du har DESC NULLS LAST i forespørgslen skal du bruge det samme i indekset, så sorteringsrækkefølgen matcher, og indekset er anvendeligt.

Effektivitet / optimering af ydeevne

Afvej omkostninger og fordele, før du opretter skræddersyede indekser for hver forespørgsel. Potentialet for ovenstående indeks afhænger i høj grad af datadistribution .

Indekset bruges, fordi det leverer forudsorterede data. I Postgres 9.2 eller nyere kan forespørgslen også drage fordel af en kun indeksscanning hvis indekset er mindre end den underliggende tabel. Indekset skal dog scannes i sin helhed.

For rækker pr. kunde (høj kardinalitet i kolonne kunde ), dette er meget effektivt. Endnu mere, hvis du alligevel har brug for sorteret output. Fordelen mindskes med et voksende antal rækker pr. kunde.
Ideelt set har du nok work_mem at behandle det involverede sorteringstrin i RAM og ikke spilde til disk. Men generelt at indstille work_mem også høj kan have negative virkninger. Overvej SET LOCAL til usædvanligt store forespørgsler. Find, hvor meget du har brug for med EXPLAIN ANALYZE . Omtale af "Disk: " i sorteringstrinnet angiver behovet for mere:

  • Konfigurationsparameter work_mem i PostgreSQL på Linux
  • Optimer simpel forespørgsel ved hjælp af BESTIL EFTER dato og tekst

For mange rækker pr. kunde (lav kardinalitet i kolonne kunde ), en løs indeksscanning (a.k.a. "skip scanning") ville være (meget) mere effektivt, men det er ikke implementeret op til Postgres 14. (En implementering til kun indeksscanninger er under udvikling til Postgres 15. Se her og her.)
For nu er der hurtigere forespørgselsteknikker at erstatte dette. Især hvis du har et separat bord med unikke kunder, hvilket er den typiske brugssag. Men også hvis du ikke gør det:

  • SELECT DISTINCT er langsommere end forventet på min tabel i PostgreSQL
  • Optimer GROUP BY-forespørgsel for at hente seneste række pr. bruger
  • Optimer gruppevis maksimal forespørgsel
  • Forespørg på de sidste N relaterede rækker pr. række

Benchmarks

Se separat svar.



  1. Send array til MySQL-lagret rutine

  2. COUNT(*) fra flere tabeller i MySQL

  3. 3 ting at vide om databaser

  4. hvordan man emulerer insert ignore og på duplikatnøgleopdatering (sql merge) med postgresql?