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

Získejte n seskupených kategorií a shrňte ostatní do jedné

Konkrétní obtížnost zde:Dotazy s jednou nebo více agregačními funkcemi v SELECT seznam a žádný GROUP BY klauzule vytvoří přesně jeden řádek, i když žádný řádek nebyl nalezen v podkladové tabulce.

V WHERE nemůžete nic dělat klauzule k potlačení tohoto řádku. Takový řádek musíte potom vyloučit , tj. v HAVING klauzuli nebo ve vnějším dotazu.

Podle dokumentace:

Pokud dotaz obsahuje volání agregovaných funkcí, ale žádné GROUP BY klauzule, stále dochází k seskupování:výsledkem je jeden řádek skupiny (nebo možná norows vůbec, pokud je pak jeden řádek odstraněn pomocí HAVING ). Totéž platí, pokud obsahuje HAVING klauzule, a to i bez volání agregačních funkcí nebo GROUP BY doložka.

Je třeba poznamenat, že přidání GROUP BY klauzule pouze s konstantním výrazem (která je jinak zcela nesmyslná!) funguje také. Viz příklad níže. Ale raději bych ten trik nepoužíval, i když je krátký, levný a jednoduchý, protože je stěží zřejmé, co dělá.

Následující dotaz vyžaduje pouze skenování jedné tabulky a vrátí 7 nejlepších kategorií seřazených podle počtu. Pokud (a pouze pokud ) existuje více kategorií, zbytek je shrnut do 'Ostatní':

WITH cte AS (
   SELECT categoryid, count(*) AS data
        , row_number() OVER (ORDER BY count(*) DESC, categoryid) AS rn
   FROM   contents
   GROUP  BY 1
   )
(  -- parentheses required again
SELECT categoryid, COALESCE(ca.name, 'Unknown') AS label, data
FROM   cte
LEFT   JOIN category ca ON ca.id = cte.categoryid
WHERE  rn <= 7
ORDER  BY rn
)
UNION ALL
SELECT NULL, 'Others', sum(data)
FROM   cte
WHERE  rn > 7         -- only take the rest
HAVING count(*) > 0;  -- only if there actually is a rest
-- or: HAVING  sum(data) > 0
  • Pokud více kategorií může mít stejný počet na 7. / 8. místě, musíte přerušit remízu. V mém příkladu kategorie s menším categoryid vyhrát takový závod.

  • Závorky musí obsahovat LIMIT nebo ORDER BY klauzule k jednotlivé větvi UNION dotaz.

  • Stačí se připojit k tabulce category pro 7 nejlepších kategorií. A v tomto scénáři je obecně levnější nejprve agregovat a připojit se později. Nepřipojujte se tedy k základnímu dotazu v CTE (společný tabulkový výraz) s názvem cte , připojte se pouze k prvnímu SELECT UNION dotaz, je to levnější.

  • Nejste si jisti, proč potřebujete COALESCE . Pokud máte cizí klíč z contents.categoryid na category.id a oba contents.categoryid a category.name jsou definovány NOT NULL (jako by pravděpodobně měly být), pak to nepotřebujete.

Liché GROUP BY true

Tohle by taky fungovalo:

...

UNION ALL
SELECT NULL , 'Others', sum(data)
FROM   cte
WHERE  rn > 7
GROUP BY true; 

A dokonce mám o něco rychlejší plány dotazů. Ale je to poněkud zvláštní hack ...

SQL Fiddle předvedení všech.

Související odpověď s podrobnějším vysvětlením pro UNION ALL / LIMIT technika:

  • Sečtěte výsledky několika dotazů a poté najděte 5 nejlepších v SQL


  1. Jak mohu odstranit duplicitní řádky v tabulce

  2. Rychlé nalezení odlišných hodnot

  3. Spojte frázi končící na předponu s fulltextovým vyhledáváním

  4. Můžete použít alias v klauzuli WHERE v mysql?