Den specifika svårigheten här:Frågor med en eller flera aggregerade funktioner i SELECT lista och ingen GROUP BY sats producera exakt en rad, även om ingen rad hittas i den underliggande tabellen.
Det finns inget du kan göra i WHERE klausul för att undertrycka den raden. Du måste utesluta en sådan rad i efterhand , det vill säga i HAVING klausul, eller i en yttre fråga.
Per dokumentation:
Om en fråga innehåller samlade funktionsanrop, men inga
GROUP BYsats, gruppering förekommer fortfarande:resultatet är en enda grupprad (eller kanske norows alls, om den enstaka raden sedan elimineras avHAVING). Detsamma gäller om den innehåller enHAVINGklausul, även utan aggregatfunktionsanrop ellerGROUP BYklausul.
Det bör noteras att lägga till en GROUP BY sats med bara ett konstant uttryck (som annars är helt meningslöst!) fungerar också. Se exempel nedan. Men jag vill helst inte använda det tricket, även om det är kort, billigt och enkelt, för det är knappast självklart vad det gör.
Följande fråga behöver bara en genomsökning av en tabell och returnerar de 7 bästa kategorierna sorterade efter antal. Om (och endast om ) det finns fler kategorier, resten sammanfattas i 'Andra':
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
-
Du måste bryta banden om flera kategorier kan ha samma antal över 7:e/8:e rangen. I mitt exempel kategorier med det mindre
categoryidvinna ett sådant lopp. -
Parenteser krävs för att inkludera en
LIMITellerORDER BYsats till en enskild del av enUNIONfråga. -
Du behöver bara gå med i tabellen
categoryför de 7 bästa kategorierna. Och det är generellt sett billigare att aggregera först och gå med senare i det här scenariot. Så gå inte med i basfrågan i CTE (common table expression) som hetercte, gå bara med i den förstaSELECTavUNIONfråga, det är billigare. -
Inte säker på varför du behöver
COALESCE. Om du har en främmande nyckel på plats fråncontents.categoryidtillcategory.idoch bådacontents.categoryidochcategory.nameär definieradeNOT NULL(som de förmodligen borde vara), då behöver du det inte.
Det udda GROUP BY true
Detta skulle också fungera:
...
UNION ALL
SELECT NULL , 'Others', sum(data)
FROM cte
WHERE rn > 7
GROUP BY true; Och jag får till och med lite snabbare frågeplaner. Men det är ett ganska udda hack ...
SQL Fiddle demonstrerar alla.
Relaterat svar med mer förklaring för UNION ALL / LIMIT teknik:
- Summera resultaten av några få frågor och hitta sedan topp 5 i SQL