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