Специфичната трудност тук:Заявки с една или повече агрегатни функции в 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