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

Zdobądź n pogrupowanych kategorii i zsumuj inne w jedną

konkretna trudność tutaj:zapytania z co najmniej jedną funkcją agregującą w SELECT lista i nie GROUP BY klauzula tworzy dokładnie jeden wiersz, nawet jeśli nie znaleziono żadnego wiersza w tabeli poniżej.

Nie możesz nic zrobić w WHERE klauzula, aby pominąć ten wiersz. Musisz wykluczyć taki wiersz po fakcie , czyli w HAVING klauzuli lub w zewnętrznym zapytaniu.

Zgodnie z dokumentacją:

Jeśli zapytanie zawiera wywołania funkcji agregujących, ale nie GROUP BY klauzula,grupowanie nadal występuje:wynikiem jest pojedynczy wiersz grupy (lub być może w ogóle norows, jeśli pojedynczy wiersz zostanie następnie wyeliminowany przez HAVING ). To samo jest prawdziwe, jeśli zawiera HAVING klauzula, nawet bez wywołań funkcji agregujących lub GROUP BY klauzula.

Należy zauważyć, że dodanie GROUP BY klauzula zawierająca tylko wyrażenie stałe (które w przeciwnym razie jest całkowicie bezcelowe!) również działa. Zobacz przykład poniżej. Ale wolałbym nie używać tej sztuczki, nawet jeśli jest krótka, tania i prosta, ponieważ nie wiadomo, do czego służy.

Następujące zapytanie wymaga tylko skanowania pojedynczej tabeli i zwraca 7 najlepszych kategorii uporządkowanych według liczby. Jeśli (i tylko jeśli ) jest więcej kategorii, reszta jest podsumowana jako „Inne”:

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
  • Musisz zerwać remisy, jeśli wiele kategorii może mieć taką samą liczbę na 7 / 8 pozycji. W moim przykładzie kategorie z mniejszym categoryid wygraj taki wyścig.

  • Nawiasy muszą zawierać LIMIT lub ORDER BY klauzula do pojedynczego odgałęzienia UNION zapytanie.

  • Musisz tylko dołączyć do tabeli category dla 7 najlepszych kategorii. W tym scenariuszu generalnie taniej jest najpierw dokonać agregacji, a później dołączyć. Dlatego nie dołączaj do podstawowego zapytania w CTE (wspólne wyrażenie tabelowe) o nazwie cte , dołącz tylko do pierwszego SELECT UNION zapytanie, to jest tańsze.

  • Nie wiem, dlaczego potrzebujesz COALESCE . Jeśli masz klucz obcy z contents.categoryid do category.id i oba contents.categoryid i category.name są zdefiniowane NOT NULL (tak jak prawdopodobnie powinny być), to nie jest to potrzebne.

Nieparzyste GROUP BY true

To też by zadziałało:

...

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

Dostaję nawet nieco szybsze plany zapytań. Ale to dość dziwny hack...

Skrzypce SQL demonstrując wszystko.

Powiązana odpowiedź z dodatkowym wyjaśnieniem dla UNION ALL / LIMIT technika:

  • Podsumuj wyniki kilku zapytań, a następnie znajdź 5 najlepszych w SQL


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. O indeksie klastrowym w postgres

  2. Dlaczego najlepiej przechowywać numer telefonu jako ciąg, a nie jako liczbę całkowitą?

  3. Postępy w aktualizacji online

  4. Alternatywy PGTune — konfiguracja ClusterControl PostgreSQL

  5. Schematy PostgreSQL / Przestrzenie nazw z Django