この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:4
ページ更新者:atom
更新日時:2026-06-11 07:07:02

タイトル: HAVING句
SEOタイトル: SQL HAVING 句完全ガイド (WHERE との違い)

この記事の要点
  • HAVING 句GROUP BY による集計結果に対するフィルタ
  • WHERE は行のフィルタ(集計前)、HAVING は集計後のフィルタ
  • HAVING COUNT(*) > 5 など集計関数を条件に使えるのは HAVING だけ
  • 性能上はWHERE で先に絞るのがセオリー(インデックス利用可能)
  • MySQL / PostgreSQL / SQL Server で標準 SQL 構文として互換

HAVING 句とは

HAVING 句GROUP BY でグループ化した結果に対して、集計値を条件にフィルタリングするための SQL 句です。WHERE 句が個々の行に対する条件なのに対し、HAVING はグループ全体に対する条件を書きます。

基本構文

SELECT カラム, 集計関数(...)
FROM テーブル
[WHERE 行のフィルタ]
GROUP BY カラム
[HAVING 集計後のフィルタ]
[ORDER BY ...]
[LIMIT ...]

SQL の処理順序は次のとおりです:

  1. FROM でテーブルを読む
  2. WHERE で行をフィルタ
  3. GROUP BY でグループ化
  4. HAVING でグループをフィルタ
  5. SELECT で列を計算
  6. ORDER BY / LIMIT

WHERE と HAVING の違い

項目WHEREHAVING
対象個々の行グループ(集計結果)
適用タイミングGROUP BY の前GROUP BY の後
集計関数使えない使える
インデックス利用可能(高速)不可(集計後)
必須条件単独で使える通常 GROUP BY 必須

典型例: 注文 5 件以上のユーザー

-- ユーザーごとの注文数を出し、5 件以上のユーザーだけ抽出
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
HAVING COUNT(*) >= 5
ORDER BY order_count DESC;

-- 結果例
-- user_id | order_count
--      42 | 12
--       7 |  8
--     128 |  5

WHERE と HAVING を併用

「2024 年以降の注文に絞り(WHERE)、ユーザーごとに集計し、合計金額 10 万超のみ(HAVING)」のように両方を使うのが定石:

SELECT
    user_id,
    COUNT(*)     AS order_count,
    SUM(amount)  AS total_amount
FROM orders
WHERE  ordered_at >= '2024-01-01'       -- 集計前: インデックス使える
GROUP BY user_id
HAVING SUM(amount) > 100000              -- 集計後: SUM を条件に
ORDER BY total_amount DESC
LIMIT 10;

集計関数を HAVING で使う

-- COUNT
HAVING COUNT(*) > 5
HAVING COUNT(DISTINCT product_id) >= 3

-- SUM / AVG
HAVING SUM(amount) > 100000
HAVING AVG(score) >= 80

-- MIN / MAX
HAVING MIN(price) < 1000
HAVING MAX(created_at) >= '2024-01-01'

-- 複数条件 (AND / OR)
HAVING COUNT(*) > 5 AND SUM(amount) > 50000
HAVING SUM(amount) > 100000 OR COUNT(*) > 10

JOIN との組み合わせ

-- ユーザー名と注文合計を出し、合計 5 万超のみ
SELECT
    u.id,
    u.name,
    COUNT(o.id)    AS order_count,
    SUM(o.amount)  AS total_amount
FROM users u
INNER JOIN orders o ON o.user_id = u.id
WHERE  u.status   = 'active'        -- WHERE: ユーザー行のフィルタ
   AND o.status   = 'completed'     -- WHERE: 注文行のフィルタ
GROUP BY u.id, u.name
HAVING SUM(o.amount) >= 50000       -- HAVING: 集計後のフィルタ
ORDER BY total_amount DESC;

サブクエリ vs HAVING

同じ結果を得るのに、HAVING を使う方法とサブクエリで書く方法があります:

-- 方法 A: HAVING
SELECT user_id, COUNT(*) AS cnt
FROM orders
GROUP BY user_id
HAVING COUNT(*) >= 5;

-- 方法 B: サブクエリ (派生テーブル)
SELECT user_id, cnt
FROM (
    SELECT user_id, COUNT(*) AS cnt
    FROM orders
    GROUP BY user_id
) t
WHERE cnt >= 5;

-- 方法 C: WHERE 内のサブクエリ
SELECT user_id
FROM users
WHERE id IN (
    SELECT user_id FROM orders
    GROUP BY user_id HAVING COUNT(*) >= 5
);

多くの DB ではオプティマイザが同等に書き換えるため性能差はわずかですが、可読性は HAVING の方が高いです。

性能上の注意: WHERE で絞れるなら WHERE で

-- ❌ 非効率: 全行を集計してから「2024 年以降」を絞る
SELECT user_id, MAX(ordered_at)
FROM orders
GROUP BY user_id
HAVING MAX(ordered_at) >= '2024-01-01';

-- ✅ 効率的: WHERE で 2024 年以降に絞ってから集計
-- (ordered_at にインデックスがあれば爆速)
SELECT user_id, MAX(ordered_at)
FROM orders
WHERE ordered_at >= '2024-01-01'
GROUP BY user_id;
-- ※ ただし「過去にしか注文してないユーザー」は出ない点に注意

-- 集計後でしか判定できない場合のみ HAVING
SELECT user_id, COUNT(*) AS cnt
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 100;  -- COUNT は集計後にしか分からない

HAVING を GROUP BY 無しで使う

標準 SQL では HAVINGGROUP BY なしでも使えます(全行が 1 グループとして扱われる)が、実用上はほぼ使いません:

-- 全件の COUNT が 100 を超えていれば 1 行返る
SELECT COUNT(*) AS total
FROM users
HAVING COUNT(*) > 100;

-- 実際にはこう書く方が分かりやすい
SELECT COUNT(*) AS total FROM users;
-- → 結果を見て判断

DB ごとの互換性

DBHAVING 対応備考
MySQL / MariaDB標準互換SELECT のエイリアスを HAVING で使える(標準外)
PostgreSQL標準互換厳密。エイリアスを HAVING で使うのは要 LATERAL 等
SQL Server標準互換SELECT エイリアス HAVING 使用不可
Oracle標準互換SELECT エイリアス HAVING 使用不可
SQLite標準互換緩い(GROUP BY 無し HAVING も寛容)

FAQ

Q: WHERE に集計関数を書ける?
A: 書けません。WHERE COUNT(*) > 5 はエラーになります。集計関数は HAVING で。

Q: HAVING でカラムエイリアスを使える?
A: MySQL は使えますが、PostgreSQL / SQL Server / Oracle は使えません。集計式そのものを書くのが互換性のためにはベストです。

Q: HAVING は遅い?
A: HAVING 自体は遅くありません。ただし WHERE で先に絞れる条件を HAVING に書くと、無駄なグループ化が発生して遅くなります。