De specifieke moeilijkheid hier:Query's met een of meer statistische functies in de SELECT
lijst en geen GROUP BY
clausule produceert precies één rij, zelfs als er geen rij is gevonden in de onderliggende tabel.
U kunt niets doen in de WHERE
clausule om die rij te onderdrukken. Je moet zo'n rij achteraf uitsluiten , d.w.z. in de HAVING
clausule, of in een buitenste vraag.
Per documentatie:
Als een query geaggregeerde functieaanroepen bevat, maar geen
GROUP BY
clausule,groepering komt nog steeds voor:het resultaat is een enkele groepsrij (of misschien helemaal geen norows, als de enkele rij dan wordt geëlimineerd doorHAVING
). Hetzelfde geldt als het eenHAVING
. bevat clausule, zelfs zonder aggregatiefunctie-aanroepen ofGROUP BY
clausule.
Opgemerkt moet worden dat het toevoegen van een GROUP BY
clausule met alleen een constante uitdrukking (die verder volkomen zinloos is!) werkt ook. Zie voorbeeld hieronder. Maar ik gebruik dat trucje liever niet, ook al is het kort, goedkoop en eenvoudig, omdat het nauwelijks duidelijk is wat het doet.
De volgende zoekopdracht heeft alleen een enkele tabelscan nodig en retourneert de top 7 categorieën gerangschikt op aantal. Als (en alleen als ) er zijn meer categorieën, de rest is samengevat in 'Overige':
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
-
Je moet de banden verbreken als meerdere categorieën hetzelfde aantal kunnen hebben op de 7e/8e rang. In mijn voorbeeld, categorieën met de kleinere
categoryid
win zo'n race. -
Haakjes zijn vereist om een
LIMIT
. op te nemen ofORDER BY
clausule naar een individuele tak van eenUNION
vraag. -
Je hoeft alleen maar lid te worden van de tabel
category
voor de top 7 categorieën. En het is over het algemeen goedkoper om eerst te aggregeren en later in dit scenario mee te doen. Dus doe niet mee aan de basisquery in de CTE (common table expression) met de naamcte
, doe alleen mee aan de eersteSELECT
van deUNION
vraag, dat is goedkoper. -
Ik weet niet zeker waarom je de
COALESCE
. nodig hebt . Als u een externe sleutel heeft vancontents.categoryid
naarcategory.id
en beidecontents.categoryid
encategory.name
zijn gedefinieerdNOT NULL
(zoals ze waarschijnlijk zouden moeten zijn), dan heb je het niet nodig.
De oneven GROUP BY true
Dit zou ook werken:
...
UNION ALL
SELECT NULL , 'Others', sum(data)
FROM cte
WHERE rn > 7
GROUP BY true;
En ik krijg zelfs iets snellere queryplannen. Maar het is een nogal vreemde hack ...
SQL Fiddle alles demonstreren.
Gerelateerd antwoord met meer uitleg voor de UNION ALL
/ LIMIT
techniek:
- Stel de resultaten van een paar zoekopdrachten bij elkaar op en vind dan de top 5 in SQL