La difficoltà specifica qui:query con una o più funzioni aggregate in SELECT
list e nessun GROUP BY
La clausola produce esattamente una riga, anche se non viene trovata alcuna riga nella tabella sottostante.
Non c'è niente che puoi fare in WHERE
clausola per sopprimere quella riga. Devi escludere tale riga dopo il fatto , cioè nel HAVING
clausola o in una query esterna.
Per documentazione:
Se una query contiene chiamate di funzioni aggregate, ma non GROUP BY
clausola, il raggruppamento si verifica ancora:il risultato è una singola riga di gruppo (o forse nessuna riga, se la singola riga viene poi eliminata da HAVING
). Lo stesso vale se contiene un HAVING
clausola, anche senza chiamate aggregatefunction o GROUP BY
clausola.
Va notato che l'aggiunta di un GROUP BY
Anche la clausola con solo un'espressione costante (che altrimenti sarebbe completamente inutile!) funziona. Vedi esempio sotto. Ma preferirei non usare quel trucco, anche se è breve, economico e semplice, perché non è affatto ovvio cosa faccia.
La query seguente richiede solo una analisi di una singola tabella e restituisce le prime 7 categorie ordinate per conteggio. Se (e solo se ) ci sono più categorie, il resto è riassunto in 'Altri':
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
-
Devi rompere i pareggi se più categorie possono avere lo stesso conteggio nel 7°/8° grado. Nel mio esempio, le categorie con il
categoryid
più piccolo vincere una gara del genere. -
Le parentesi sono necessarie per includere un
LIMIT
oORDER BY
clausola a una singola gamba di unaUNION
interrogazione. -
Devi solo iscriverti alla tabella
category
per le prime 7 categorie. Ed è generalmente più economico aggregare prima e unirsi successivamente in questo scenario. Quindi non unirti alla query di base nella CTE (espressione di tabella comune) denominatacte
, partecipa solo al primoSELECT
dellaUNION
query, è più economico. -
Non sono sicuro del motivo per cui hai bisogno del
COALESCE
. Se disponi di una chiave esterna dacontents.categoryid
acategory.id
ed entrambicontents.categoryid
ecategory.name
sono definitiNOT NULL
(come probabilmente dovrebbero essere), allora non ne hai bisogno.
Lo dispari GROUP BY true
Funzionerebbe anche questo:
...
UNION ALL
SELECT NULL , 'Others', sum(data)
FROM cte
WHERE rn > 7
GROUP BY true;
E ottengo anche piani di query leggermente più veloci. Ma è un trucco piuttosto strano...
SQL Fiddle dimostrando tutto.
Risposta correlata con ulteriori spiegazioni per UNION ALL
/ LIMIT
tecnica:
- Somma i risultati di alcune query e poi trova le prime 5 in SQL