GROUP BYで迷わない──WHEREとHAVING、どちらで絞る?現場で差がつく“実行順序”理解法
GROUP BYで集計しようとしたら、エラー?思った値が出ない?
…そんな“つまずき”に、私たちは何度も出くわします。
なぜWHEREとHAVINGの使い分けで迷うのか?
──答えは「SQLの実行順序」を“腹落ち”で理解できていないからです。
私たち開発者が現場でハマる理由と、明日から使える“脱落しないGROUP BY”テクを図解と共に解説します。
用語解説:GROUP BY
SQLでデータを特定の列ごとにまとめて集計するための構文。売上や件数など、グループ単位で計算したいときに使う。用語解説:WHERE
SQLでデータを抽出・絞り込みするための条件指定句。グループ化や集計の前段階で使う。用語解説:HAVING
GROUP BYでグループ化・集計した後のデータに対して条件を指定し、さらに絞り込むための句。用語解説:SQL
データベースを操作するための言語。データの検索・集計・更新・削除などを行う。(SQLの基本やSELECT文の実行順については『初心者向けSQL講座|SELECT文の書き方と実行順を完全解説』をご参照ください)
GROUP BYとは?――生データを“まとめて要約”するSQLの第一歩
GROUP BYは「特定の列ごとにデータをグループ化し、集計する」ためのSQL句です。
たとえば、下記の売上テーブルを例に見てみましょう。
用語解説:テーブル
データベース内でデータを行と列で管理するための表。Excelの表のようなイメージ。
| order_id | order_date | product_category | total_price |
|----------|------------|------------------|-------------|
| 1 | 2023-04-01 | 書籍 | 2000 |
| 2 | 2023-04-02 | 家電 | 50000 |
| 3 | 2023-04-03 | 書籍 | 1500 |
| 4 | 2023-05-10 | 食品 | 500 |
| 5 | 2023-05-12 | 家電 | 30000 |
このままでは「カテゴリ別の売上合計」が見えません。
GROUP BYなら、「書籍」「家電」…のカテゴリごとにまとめ、合計・件数などをすぐ算出できます。
用語解説:集計関数(SUM, COUNTなど)
データの合計(SUM)、件数(COUNT)、平均(AVG)などを計算するSQLの関数。GROUP BYと組み合わせて使う。(SQLの集計関数やNULLの扱いについては『SQL NULL完全攻略|IS NULL・COALESCEの使い分けとDB別バグ防止術5選』もご参照ください)
“バラバラなデータを“意味あるかたまり”に整理し、集計する”
これがGROUP BYの本質です。
1. なぜ混乱?WHEREとHAVINGの違いは「SQLの処理順」にあり
私たちがつまずきやすいのが、WHEREとHAVINGの使い分け。
この違い、どう説明できますか?
ポイントは「SQLが内部でどんな順番で処理されるか」です。
実は、書いた通りの順ではなく、下記の“7ステップ”で実行されています。
【図解】SQLクエリの7段階処理フロー
- FROM … どのテーブルか
- JOIN … 必要なら結合
- WHERE … グループ化前に元データを絞り込む
- GROUP BY … 指定列でグループ化
- HAVING … グループ化後の集計値で絞り込む
- SELECT … 何を出すか(集計もここ)
- ORDER BY … 並び順を決める
用語解説:FROM
どのテーブルからデータを取得するかを指定するSQL句。用語解説:JOIN
複数のテーブルを結合し、関連するデータをまとめて取得するためのSQL句。用語解説:SELECT
どの列(項目)を出力するかを指定するSQL句。集計関数もここで使う。用語解説:ORDER BY
結果の並び順を指定するSQL句。昇順・降順などを指定できる。
(処理の流れイメージ図をここに)
“どのタイミングでデータを絞るか”が、WHEREとHAVINGの決定的な差です。
- WHERE:集計・グループ化前。 SUM()やCOUNT()など集計関数は使えない。
- HAVING:グループ化後。 SUM()など集計済みの値で絞り込む。
(WHERE・HAVING・JOINの違いと実践的な使い分けは『SQL IN・EXISTS・JOINの違い徹底解説|安全で速い選び方と実践7チェック』もご参照ください)
料理で例えると…
WHEREは「材料の下ごしらえ」。
HAVINGは「完成した料理の合格チェック」です。
2. これで腹落ち!現場でよく使うGROUP BYパターン3選
実際に、売上データで「GROUP BYの基本→応用→HAVING絞り込み」まで流れを追いましょう。
2-1. 基本:「カテゴリごとに件数」を出す(COUNT)
SELECT
product_category,
COUNT(order_id) AS order_count
FROM
sales
GROUP BY
product_category;
実行例:
| product_category | order_count |
|------------------|------------|
| 書籍 | 2 |
| 家電 | 2 |
| 食品 | 1 |
- GROUP BYで「書籍・家電・食品」ごとにグループ
- COUNTで各グループの件数を取得
2-2. 応用:「カテゴリ・月ごとの売上合計」を出す(複数列GROUP BY, SUM)
SELECT
product_category,
STRFTIME('%Y-%m', order_date) AS sales_month, -- SQLiteの場合
SUM(total_price) AS monthly_sales
FROM
sales
GROUP BY
product_category, sales_month;
用語解説:複数列GROUP BY
GROUP BYで2つ以上の列を指定すると、「カテゴリ×月」など複数軸でグループ化できる。
実行例:
| product_category | sales_month | monthly_sales |
|------------------|-------------|--------------|
| 書籍 | 2023-04 | 3500 |
| 家電 | 2023-04 | 50000 |
| 家電 | 2023-05 | 30000 |
| 食品 | 2023-05 | 500 |
複数列でグループ化すれば、「カテゴリ×月」単位の集計も簡単です。
2-3. 実践:売上が10万円以上のカテゴリだけ抽出(HAVING)
SELECT
product_category,
SUM(total_price) AS total_sales
FROM
sales
GROUP BY
product_category
HAVING
SUM(total_price) >= 100000;
- GROUP BY後の集計値(ここではSUM)で絞るなら、HAVINGが必須
- WHEREでSUMを書くとエラーになる理由は、“集計前”だから
3. GROUP BYでよく出るエラー──原因と解決策
「SELECTに書いた列がGROUP BYにない」
「WHEREでSUM()したらエラー」
…私たちが現場で“焦る”ポイントを整理します。
3-1. SELECTリストの列がGROUP BYに無い
-- ❌ エラー例
SELECT product_category, product_name
FROM sales
GROUP BY product_category;
用語解説:SELECTリスト
SELECT句で指定した出力対象の列の一覧。GROUP BYに含まれない列は集約関数でまとめる必要がある。
原因:
「書籍」グループの中に複数のproduct_nameがあり、どれを出すか決められないから。
解決:
-- ✅ product_nameもグループ化
SELECT product_category, product_name
FROM sales
GROUP BY product_category, product_name;
3-2. WHERE句で集計関数を使っている
-- ❌ エラー例
SELECT product_category, SUM(total_price)
FROM sales
WHERE SUM(total_price) > 10000
GROUP BY product_category;
用語解説:WHERE句と集計関数の関係
WHERE句はグループ化前のデータにしか使えず、SUMやCOUNTなどの集計関数はHAVING句で使う。
原因:
WHERE句は“グループ化前”のデータにしかアクセスできず、SUM値はまだ無いから。
解決:
-- ✅ HAVINGで絞る
SELECT product_category, SUM(total_price) AS total_sales
FROM sales
GROUP BY product_category
HAVING SUM(total_price) > 10000;
4. “GROUP BY迷子”を脱却するための3ポイントまとめ
- WHEREは素材選び:グループ化前のデータを絞り込む
- GROUP BYで束ねる:指定した列でグループ化する
- HAVINGは完成品選び:グループ化後の集計値で絞る
これを押さえれば、SELECTに何を書く?WHERE?HAVING?…で迷うことはなくなります。
ぜひコードをコピペして、まずは動かしてみてください。
FAQ
- Q. NULLを含む列をGROUP BYすると?
→ NULLも1つの独立したグループになります。 -
Q. GROUP BYとウィンドウ関数(PARTITION BY)の違いは?
→
GROUP BYは集約して行数が減る、ウィンドウ関数は元の行数そのままで集計値が付与されます。 - Q. HAVING句で複数条件は?
→ AND/ORで複数条件可
例:HAVING SUM(total_price) > 10000 AND COUNT(order_id) >= 2 - Q. GROUP BYとDISTINCTの使い分けは?
→ DISTINCTは重複除去、GROUP BYは集計やグループ単位で計算したいとき。 - Q. 集計値で並び替えたい
→ ORDER BY SUM(total_price) DESC などでOK。 - Q. なぜGROUP BYの後にWHEREは使えない?
→ SQLはWHERE→GROUP BYの順で処理。
つまり、“未来の値”で元データを絞ることはできません。
用語解説:NULL
データが存在しないことを示す特別な値。GROUP BYではNULLも1つのグループとして扱われる。用語解説:ウィンドウ関数(PARTITION BY)
元の行数を保ったまま、グループごとの集計値を各行に付与できるSQLの機能。用語解説:DISTINCT
重複するデータを除外し、一意な値だけを抽出するSQLのキーワード。(DISTINCT・GROUP BY・ROW_NUMBERの違いは『SQL重複除外のベストプラクティス徹底比較|DISTINCT・GROUP BY・ROW_NUMBERの違いは?』もご参照ください)
「SQLの実行順序」を知れば、もうGROUP BYは怖くない──明日から集計クエリを“自信を持って”書けるようになります。