PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Вземете n групирани категории и сумирайте останалите в една

Специфичната трудност тук:Заявки с една или повече агрегатни функции в SELECT списък и без GROUP BY клауза произвежда точно един ред, дори ако не е намерен ред в основната таблица.

Не можете да направите нищо в WHERE клауза за потискане на този ред. Трябва да изключите такъв ред след факта , т.е. в HAVING клауза или във външна заявка.

По документация:

Ако заявка съдържа извиквания на обобщени функции, но не GROUP BY клауза, групирането все още се случва:резултатът е единичен групов ред (или може би въобще norows, ако единичният ред бъде елиминиран чрез HAVING ). Същото е вярно, ако съдържа HAVING клауза, дори без извиквания на агрегатни функции или GROUP BY клауза.

Трябва да се отбележи, че добавянето на GROUP BY клауза само с константен израз (който иначе е напълно безсмислен!) също работи. Вижте примера по-долу. Но предпочитам да не използвам този трик, дори и да е кратък, евтин и прост, защото едва ли е очевидно какво прави.

Следната заявка се нуждае само от сканиране на една таблица и връща първите 7 категории, подредени по брой. Ако (и само ако ) има още категории, останалото е обобщено в „Други“:

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
  • Трябва да прекъснете равенството, ако няколко категории могат да имат еднакъв брой в 7-ми/8-ия ранг. В моя пример, категории с по-малкия categoryid спечелете такова състезание.

  • Скобите са задължителни, за да включват LIMIT или ORDER BY клауза към отделна част от UNION заявка.

  • Трябва само да се присъедините към таблица category за първите 7 категории. И като цяло е по-евтино първо да се агрегирате и да се присъедините по-късно в този сценарий. Така че не се присъединявайте към основната заявка в CTE (израз за обща таблица) с име cte , присъединете се само в първия SELECT на UNION запитване, това е по-евтино.

  • Не сте сигурни защо имате нужда от COALESCE . Ако имате външен ключ от contents.categoryid към category.id и двете contents.categoryid и category.name са дефинирани NOT NULL (както вероятно трябва да бъдат), тогава нямате нужда от него.

Нечетното GROUP BY true

Това също би работило:

...

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

И дори получавам малко по-бързи планове за заявка. Но това е доста странен хак...

SQL Fiddle демонстрира всичко.

Свързан отговор с повече обяснения за UNION ALL / LIMIT техника:

  • Сложете резултатите от няколко заявки и след това намерете първите 5 в 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. Отказ за репликация на PostgreSQL 101

  2. Еквивалент на PostgreSQL DATEADD().

  3. Преглед на инструментите за планиране на работа за PostgreSQL

  4. Как да напишете заявка, нечувствителна към главните букви и за MySQL, и за Postgres?

  5. Управление на високата наличност на PostgreSQL – Част I:Автоматично отказване на PostgreSQL