ここでの具体的な問題SELECT:リストに 1 つ以上の集計関数があり、GROUP BY句がないクエリでは、基になるテーブルに行が見つからない場合でも、1 つの行が生成されます。
WHEREその行を抑制するために句でできることは何もありません。そのような行は、事後、つまりHAVING句内、または外部クエリで除外する必要があります。
ドキュメントごと:
クエリに集約関数の呼び出しが含まれていてもGROUP BY句がない場合でも、グループ化は行われます。結果は単一のグループ行になります (または、単一の行が によって削除された場合は、行がまったくない可能性がありますHAVING)。HAVING集計関数の呼び出しや句がなくても、句が含まれている場合も同様ですGROUP BY。
GROUP BY定数式のみを含む節を追加すること (それ以外の場合は完全に無意味です!) も機能することに注意してください。以下の例を参照してください。しかし、たとえそれが短く、安価で、単純であっても、私はむしろそのトリックを使用したくありません。
次のクエリは、1 回のテーブル スキャンのみを必要とし、カウント順に並べられた上位 7 つのカテゴリを返します。さらにカテゴリがある場合 (およびその場合のみ)、残りは「その他」にまとめられます。
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
7 位 / 8 位で複数のカテゴリが同じ数になる場合は、タイを破る必要があります。私の例では、小さいカテゴリがそのcategoryidような競争に勝ちます。
LIMITor句をクエリORDER BYの個々のレッグに含めるには、括弧が必要です。UNION
category上位 7 カテゴリのテーブルに参加するだけです。また、このシナリオでは、最初に集約し、後で結合する方が一般的に安価です。したがって、という名前のCTE (共通テーブル式)cteのベース クエリに参加しないでください。最初SELECTのUNIONクエリにのみ参加してください。これは安価です。
が必要な理由がわかりませんCOALESCE。fromとcontents.categoryidtocategory.idの両方が定義されている(おそらくそうあるべきであるように) 適切な場所に外部キーがある場合、それは必要ありません。contents.categoryidcategory.nameNOT NULL
奇妙なGROUP BY true
これもうまくいきます:
...
UNION ALL
SELECT NULL , 'Others', sum(data)
FROM cte
WHERE rn > 7
GROUP BY true;
また、わずかに高速なクエリ プランも得られます。しかし、それはかなり奇妙なハックです...
すべてを示すSQL Fiddle 。
UNION ALL/LIMITテクニックの詳細な説明を含む関連回答: