【SQL】HAVINGでグループ集計結果を絞り込む|WHEREとの違いを整理

SQL

こんにちは、かつコーチです。

前回の記事「GROUP BYでグループごとに集計する」で、GROUP BYを使うとグループ単位の集計ができることを解説しました。

そこで一つ壁にぶつかった方も多いのではないでしょうか。

「集計した結果を条件で絞り込みたいのに、WHEREを使うとエラーになる」という壁です。

この記事では、その壁を突破するHAVING句と、WHEREとの違いを整理して解説します。

HAVINGとは?

グループ化した後の結果を絞り込む句

HAVINGとは、GROUP BYでグループ化した後の集計結果に対して、条件を指定して絞り込むための句です。

前回のサンプルテーブルを使って、「注文件数が2件以上の顧客だけを表示したい」というケースを考えてみます。

SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id
HAVING COUNT(*) >= 2;

WHEREと似た書き方に見えますが、HAVINGはCOUNT(*)のような集計関数の結果に対して条件を指定できる点が大きな違いです。

なぜWHEREではなくHAVINGが必要なのか

SQLはFROM → WHERE → GROUP BY → HAVING → SELECTという順序で実行されます。

WHEREはグループ化される「前」の生の行に対する条件で、集計関数の結果(グループ化した後にしか存在しない値)はまだ計算されていません。

そのため、WHERE句の中でCOUNT(*)のような集計関数を使うと、MySQLではERROR 1111: Invalid use of group functionというエラーになります。

HAVINGはGROUP BYの「後」に実行されるため、集計関数の結果を条件に使えるというわけです。

WHEREとHAVINGの違いを比較する

実行タイミングの違い

WHEREHAVING
実行タイミングGROUP BYの前GROUP BYの後
対象個々の行グループ化された集計結果
集計関数の使用不可(エラーになる)可能
主な用途元データの絞り込み集計結果の絞り込み

この表がそのまま「どちらを使うべきか」の判断軸になります。

「集計する前の生データを絞りたいならWHERE」「集計した後の結果を絞りたいならHAVING」と覚えておけば迷いません。

WHEREとHAVINGを併用する

実務では、WHEREとHAVINGを同じクエリの中で併用することがよくあります。

SELECT customer_id, COUNT(*) AS order_count, SUM(total_amount) AS total_spent
FROM orders
WHERE order_date >= '2026-08-01'
GROUP BY customer_id
HAVING SUM(total_amount) >= 5000;

このクエリは、「2026年8月1日以降の注文(WHERE)」を対象に「顧客ごとの合計購入額が5000円以上(HAVING)」の顧客だけを抽出しています。

WHEREで先に対象データを絞り込んでからGROUP BYで集計する方が、無駄な行を集計しない分、処理も効率的になります。

つまずきやすい設定・注意点

エイリアスをHAVINGで使えるかどうか

❌ Before:SELECT句で付けたエイリアス(別名)を、そのままWHERE句で使おうとしてエラーになる

SELECT customer_id, SUM(total_amount) AS total_spent
FROM orders
WHERE total_spent >= 5000
GROUP BY customer_id;

MySQLはこのクエリを実行するとERROR 1054: Unknown column 'total_spent' in 'where clause'を返します。

WHEREが実行される段階では、まだSELECT句が処理されておらず、total_spentというエイリアスが存在しないためです。

✅ After:HAVINGならエイリアスを使える(MySQLの場合)

SELECT customer_id, SUM(total_amount) AS total_spent
FROM orders
GROUP BY customer_id
HAVING total_spent >= 5000;

MySQLではHAVING句にエイリアスを書いても動作しますが、これはMySQL独自の拡張的な挙動です。

私が以前PostgreSQL環境の案件を担当したとき、MySQLで動いていたこの書き方をそのまま流用したらエラーになり、標準的な書き方であるHAVING SUM(total_amount) >= 5000に修正した経験があります。

将来的に別のDB製品への移行を見据えるなら、エイリアスに頼らず集計関数をそのまま書くクセをつけておくと安全です。

HAVINGだけでGROUP BYを省略できるか

HAVINGは基本的にGROUP BYとセットで使いますが、GROUP BYを省略してテーブル全体を1つのグループとして扱うことも文法上は可能です。

SELECT COUNT(*) AS order_count
FROM orders
HAVING COUNT(*) >= 100;

ただし可読性が落ちるため、実務ではあまり使われません。

基本的には「HAVINGはGROUP BYとセットで使うもの」と覚えておけば十分です。

応用・一歩先の使い方

複数の集計条件を組み合わせる

HAVING句にはANDやORを使って、複数の条件を組み合わせることもできます。

SELECT customer_id, COUNT(*) AS order_count, SUM(total_amount) AS total_spent
FROM orders
GROUP BY customer_id
HAVING order_count >= 2 AND total_spent >= 8000;

「注文件数2件以上」かつ「合計購入額8000円以上」という、複数の集計条件を同時に満たすグループだけを抽出できます。

優良顧客の抽出条件を細かく設定したい場面で活躍します。

サブクエリとの組み合わせへ

HAVINGはGROUP BYの結果を絞り込む強力な手段ですが、「他のテーブルの集計結果と比較したい」といった、より複雑な条件になると力不足を感じる場面も出てきます。

そうしたケースではサブクエリ(クエリの中に埋め込む別のクエリ)が役立ちます。

次回の記事「サブクエリの基本:クエリの中にクエリを書く」で詳しく解説します。

まとめ

この記事のポイント

  • HAVINGはGROUP BYで集計した後の結果を絞り込む句
  • WHEREはグループ化前の生データ、HAVINGはグループ化後の集計結果が対象
  • WHEREとHAVINGは併用でき、先にWHEREで絞り込んでからGROUP BY・HAVINGを使うと効率的
  • MySQLではHAVINGにエイリアスを使えるが、標準的な書き方ではないため注意する

次に読むべき記事

集計結果をさらに柔軟に扱いたい方は、次回の「サブクエリの基本:クエリの中にクエリを書く」に進んでください。


タグ: SQL, 中級者向け, 集計

タイトルとURLをコピーしました