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 BY
sats, 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 enHAVING
klausul, även utan aggregatfunktionsanrop ellerGROUP BY
klausul.
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
categoryid
vinna ett sådant lopp. -
Parenteser krävs för att inkludera en
LIMIT
ellerORDER BY
sats till en enskild del av enUNION
fråga. -
Du behöver bara gå med i tabellen
category
fö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örstaSELECT
avUNION
frå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.categoryid
tillcategory.id
och bådacontents.categoryid
ochcategory.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