Die spezifische Schwierigkeit hier:Abfragen mit einer oder mehreren Aggregatfunktionen im SELECT Liste und kein GROUP BY -Klausel erzeugt genau eine Zeile, auch wenn keine Zeile gefunden wird in der zugrunde liegenden Tabelle.
In WHERE können Sie nichts tun -Klausel, um diese Zeile zu unterdrücken. Sie müssen eine solche Zeile im Nachhinein ausschließen , also im HAVING -Klausel oder in einer äußeren Abfrage.
Per Dokumentation:
Wenn eine Abfrage aggregierte Funktionsaufrufe enthält, aber kein GROUP BY -Klausel findet die Gruppierung dennoch statt:Das Ergebnis ist eine einzelne Gruppenzeile (oder vielleicht überhaupt keine Zeilen, wenn die einzelne Zeile dann durch HAVING eliminiert wird ). Dasselbe gilt, wenn es ein HAVING enthält -Klausel, auch ohne Aggregatfunktionsaufrufe oder GROUP BY Klausel.
Es sollte beachtet werden, dass das Hinzufügen eines GROUP BY Klausel mit nur einem konstanten Ausdruck (was sonst völlig sinnlos ist!) funktioniert auch. Siehe Beispiel unten. Aber ich würde diesen Trick lieber nicht anwenden, auch wenn er kurz, billig und einfach ist, weil es kaum offensichtlich ist, was er tut.
Die folgende Abfrage benötigt nur einen einzelnen Tabellenscan und gibt die Top-7-Kategorien sortiert nach Anzahl zurück. Wenn (und nur wenn ) gibt es mehr Kategorien, der Rest ist in 'Andere' zusammengefasst:
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
-
Sie müssen Gleichstände aufheben, wenn mehrere Kategorien auf dem 7./8. Rang die gleiche Anzahl haben können. In meinem Beispiel Kategorien mit der kleineren
categoryidein solches Rennen gewinnen. -
Klammern müssen ein
LIMITenthalten oderORDER BY-Klausel zu einem einzelnen Zweig einerUNIONAbfrage. -
Sie müssen nur der Tabelle
categorybeitreten für die Top 7 Kategorien. Und in diesem Szenario ist es im Allgemeinen billiger, zuerst zu aggregieren und später beizutreten. Schließen Sie sich also nicht der Basisabfrage im CTE (allgemeiner Tabellenausdruck) mit dem Namenctean , nur beim erstenSELECTmitmachen derUNIONabfragen, das ist billiger. -
Nicht sicher, warum Sie
COALESCEbenötigen . Wenn Sie einen Fremdschlüssel voncontents.categoryidhaben zucategory.idund sowohlcontents.categoryidundcategory.namesindNOT NULLdefiniert (wie sie wahrscheinlich sein sollten), dann brauchen Sie es nicht.
Das ungerade GROUP BY true
Das würde auch funktionieren:
...
UNION ALL
SELECT NULL , 'Others', sum(data)
FROM cte
WHERE rn > 7
GROUP BY true; Und ich bekomme sogar etwas schnellere Abfragepläne. Aber es ist ein ziemlich seltsamer Hack ...
SQL-Geige alles demonstrieren.
Zugehörige Antwort mit mehr Erklärung für UNION ALL / LIMIT Technik:
- Summiere die Ergebnisse einiger Abfragen und finde dann die Top 5 in SQL