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
categoryid
ein solches Rennen gewinnen. -
Klammern müssen ein
LIMIT
enthalten oderORDER BY
-Klausel zu einem einzelnen Zweig einerUNION
Abfrage. -
Sie müssen nur der Tabelle
category
beitreten 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 Namencte
an , nur beim erstenSELECT
mitmachen derUNION
abfragen, das ist billiger. -
Nicht sicher, warum Sie
COALESCE
benötigen . Wenn Sie einen Fremdschlüssel voncontents.categoryid
haben zucategory.id
und sowohlcontents.categoryid
undcategory.name
sindNOT NULL
definiert (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