sql >> Databáze >  >> RDS >> PostgreSQL

Vybrat první řádek v každé skupině GROUP BY?

DISTINCT ON je obvykle nejjednodušší a nejrychlejší v PostgreSQL .
(Optimalizace výkonu pro určité úlohy viz níže.)

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

Nebo kratší (pokud to není tak jasné) s pořadovými čísly výstupních sloupců:

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

Pokud total může být NULL (nebude to na škodu, ale budete chtít porovnat existující indexy):

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

Hlavní body

DISTINCT ON je rozšíření PostgreSQL standardu (kde pouze DISTINCT v celku SELECT seznam je definován).

Uveďte libovolný počet výrazů v DISTINCT ON klauzule, kombinovaná hodnota řádku definuje duplikáty. Manuál:

Je zřejmé, že dva řádky jsou považovány za odlišné, pokud se liší alespoň v jedné hodnotě sloupce. Nulové hodnoty jsou v tomto srovnání považovány za stejné.

Tučné zdůraznění moje.

DISTINCT ON lze kombinovat s ORDER BY . Úvodní výrazy v ORDER BY musí být v sadě výrazů v DISTINCT ON , ale pořadí mezi nimi můžete libovolně uspořádat. Příklad.
Můžete přidat další výrazy do ORDER BY vybrat konkrétní řádek z každé skupiny vrstevníků. Nebo, jak to uvádí manuál:

DISTINCT ON výraz(y) se musí shodovat s ORDER BY zcela vlevo výraz(y). ORDER BY klauzule bude normálně obsahovat další výraz(y), které určují požadovanou prioritu řádků v rámci každého DISTINCT ON skupina.

Přidal jsem id jako poslední položka pro přerušení remízy:
"Vyberte řádek s nejmenším id z každé skupiny sdílející nejvyšší total ."

Chcete-li seřadit výsledky způsobem, který nesouhlasí s pořadím řazení určujícím první ve skupině, můžete vnořit nad dotaz do vnějšího dotazu s jiným ORDER BY . Příklad.

Pokud total může být NULL, vy s největší pravděpodobností chcete řádek s největší nenulovou hodnotou. Přidejte NULLS LAST jako předvedeno. Viz:

  • Seřadit podle sloupce ASC, ale nejprve hodnoty NULL?

Položka SELECT seznam není omezeno výrazy v DISTINCT ON nebo ORDER BY jakýmkoliv způsobem. (Není potřeba ve výše uvedeném jednoduchém případě):

  • Nemusíte zahrnout jakýkoli z výrazů v DISTINCT ON nebo ORDER BY .

  • můžete zahrnout jakýkoli jiný výraz do SELECT seznam. To je užitečné pro nahrazení mnohem složitějších dotazů poddotazy a agregačními / okenními funkcemi.

Testoval jsem s Postgres verzemi 8.3 – 13. Ale tato funkce tu byla minimálně od verze 7.1, takže v podstatě vždy.

Index

Perfektní index pro výše uvedený dotaz by byl vícesloupcový index zahrnující všechny tři sloupce ve shodném pořadí a s odpovídajícím pořadím řazení:

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

Může být příliš specializovaný. Použijte jej však, pokud je výkon čtení pro konkrétní dotaz zásadní. Pokud máte DESC NULLS LAST v dotazu použijte totéž v indexu, aby se pořadí řazení shodovalo a index byl použitelný.

Optimalizace efektivity / výkonu

Před vytvořením přizpůsobených indexů pro každý dotaz zvažte náklady a přínosy. Potenciál výše uvedeného indexu do značné míry závisí na distribuci dat .

Index se používá, protože poskytuje předem setříděná data. V Postgresu 9.2 nebo novějším může dotaz těžit také z skenování pouze indexu pokud je index menší než podkladová tabulka. Index však musí být naskenován celý.

Pro málo řádků na zákazníka (vysoká mohutnost ve sloupci customer ), je to velmi efektivní. Ještě více, pokud přesto potřebujete seřazený výstup. Výhoda se snižuje s rostoucím počtem řádků na zákazníka.
V ideálním případě máte dostatek work_mem zpracovat příslušný krok řazení v paměti RAM a nerozsypat se na disk. Ale obecně nastavení work_mem také vysoká může mít nepříznivé účinky. Zvažte možnost SET LOCAL pro mimořádně velké dotazy. Zjistěte, kolik potřebujete, pomocí EXPLAIN ANALYZE . Zmínka o „Disk: " v kroku řazení označuje potřebu více:

  • Konfigurační parametr work_mem v PostgreSQL na Linuxu
  • Optimalizujte jednoduchý dotaz pomocí ORDER BY data a textu

Pro mnoho řádků na zákazníka (nízká mohutnost ve sloupci customer ), uvolněný index skenování (také znám jako "skip scan") by bylo (mnohem) efektivnější, ale to není implementováno až do Postgres 14. (Implementace pro skenování pouze s indexem je ve vývoji pro Postgres 15. Viz zde a zde.)
Pro nyní existují rychlejší techniky dotazů nahradit toto. Zejména pokud máte samostatný stůl s unikátními zákazníky, což je typický případ použití. Ale také pokud ne:

  • SELECT DISTINCT je na mém stole v PostgreSQL pomalejší, než se očekávalo
  • Optimalizujte dotaz GROUP BY pro načtení posledního řádku na uživatele
  • Optimalizujte maximální počet dotazů ve skupině
  • Dotaz na posledních N souvisejících řádků na řádek

Srovnávací hodnoty

Viz samostatná odpověď.



  1. Jak funguje FORMAT() v MariaDB

  2. ORA-12519 TNS:Nebyl nalezen žádný vhodný obslužný program

  3. CURRENT_TIMESTAMP Příklady v SQL Server (T-SQL)

  4. Jak analyzovat stav databázových indexů