グループに条件を付ける - HAVING
グループ化後の結果に対する絞り込み。WHERE との違いと使い分けを押さえる
概念図
構文
sql
SELECT 列, 集約(...) FROM t GROUP BY 列 HAVING 集約(...) 条件サンプル
10 名以上の部署だけを抽出する
sql
SELECT department_id, COUNT(*) AS cnt
FROM employees
GROUP BY department_id
HAVING COUNT(*) >= 10;HAVING とは
HAVING は GROUP BY でまとめた グループ単位の集約結果に対して条件を課す句です。SUM(amount) > 1000 や COUNT(*) >= 5 のように、集約関数の結果で絞り込みたいときに使います。
WHERE との違い
実行順序は概ね FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY です。
- WHERE: 集約前の 個々の行 に対する絞り込み。ここで落とせる行は先に落とした方が速い
- HAVING: 集約後の グループ に対する絞り込み。集約関数が使える唯一の絞り込み節
「アクティブなユーザーの中で」→ WHERE、「購入金額合計が 1 万円超のユーザー」→ HAVING、と分けて考えると迷いません。
sql
-- アクティブユーザーに限定しつつ、合計購入額が10000超のユーザーだけ
SELECT user_id, SUM(amount) AS total
FROM orders
WHERE status = 'active' -- 行レベルの絞り込み
GROUP BY user_id
HAVING SUM(amount) > 10000; -- グループレベルの絞り込みGROUP BY なしの HAVING
GROUP BY を書かずに HAVING だけを書くと、テーブル全体が 1 つのグループとして扱われます。使う場面は少ないですが、「合計が閾値を超えているときだけ 1 行返す」といった監視クエリに使えます。
よくある落とし穴
- WHERE に集約関数を書いてしまう:
WHERE SUM(x) > 100はエラー。HAVING に書く - HAVING にエイリアスが使えない DB: PostgreSQL は SELECT のエイリアスを HAVING で使えないので、式を書き直すか CTE にする必要がある
- 行レベルの条件を HAVING に書いてしまう: 集約前に落とせる条件は WHERE に書いた方がスキャン量が減り高速
