【SQL】HAVING句の書き方と使い方の基本

SQLのGROUP BYで集計した結果に対し、さらに条件を指定してデータを絞り込むHAVING句の基本を解説します。グループ化する前のデータを絞るWHERE句との違いを明確にし、比較演算子やIN句などを使った様々な条件の書き方をサンプルコード付きで分かりやすく紹介します。

作成日: 更新日:

開発環境

  • OS: Windows10
  • DatabaseGUI: TablePlus
  • MySQL: 8.0.42

HAVING句とは

HAVING句は、GROUP BY句と一緒に使い、グループ化された後のデータに対して条件を指定するためのSQL句です。

GROUP BY句は、指定した列の値が同じデータをひとつのグループにまとめる機能を持っています。HAVING句は、そのようにして作られたグループの中から、さらに特定の条件を満たすグループだけを絞り込む際に使用します。

例えば、部署ごとに集計した平均給与データの中から、「平均給与が500万円以上の部署だけ」を表示したい、といった集計結果に対する絞り込みが可能です。

よく似た機能を持つWHERE句は、グループ化される前の個々のデータに対して条件を指定します。一方でHAVING句は、グループ化された後の集計結果に対して条件を指定するという明確な違いがあります。

HAVING句の基本構文

HAVING句は、GROUP BY句でグループ化した結果に対して、さらに条件を指定してデータを絞り込むために使用します。

WHERE句がグループ化される「前」の個々の行データに対して条件を指定するのに対し、HAVING句はグループ化された「後」の集計結果に対して条件を指定する、という点が大きな違いです。

このため、HAVING句の条件式では、COUNT()SUM()AVG()といった集計関数を利用することができます。「件数が10件以上のグループのみ表示する」や「合計金額が50000円以上の部門のみ抽出する」といった処理を実現する場合に役立ちます。

HAVING句は必ずGROUP BY句の後に記述する必要があります。

1SELECT
2    グルーピングをするカラム,
3    集計関数( 集計対象カラム )
4FROM
5    テーブル名
6WHERE
7    グループ化「前」の条件式
8GROUP BY
9    グルーピングをするカラム
10HAVING
11    グループ化「後」の条件式
12ORDER BY
13    カラム名 [ASC|DESC];

サンプルコード

データベースから特定の条件で絞り込んだデータを取得する際に、HAVING句は非常に強力なツールです。HAVING句は、GROUP BY句によってグループ化されたデータに対して、さらに条件を指定するために使用します。

ここでは、様々な演算子を使ったHAVING句の具体的な使い方をサンプルコードと共に解説します。

sql/chapter07/01.sql

比較演算子(=, !=, >, <, >=, <=)は、集計結果の値が特定の値と等しいか、異なるか、大きいか、小さいかなどを判定するために使用します。

以下のサンプルでは、studentsテーブルをclass_idでグループ化し、各クラスの生徒数をCOUNT(*)で集計しています。その集計結果に対して、比較演算子を使って条件を指定しています。

1-- 比較演算子(=, !=, >, <, >=, <=)
2-- 生徒数が4人のクラスを抽出します
3SELECT
4	class_id,
5	COUNT(*) AS count_students
6FROM
7	students
8GROUP BY
9	class_id
10HAVING
11	COUNT(*) = 4;
12
13-- 生徒数が4人ではないクラスを抽出します
14SELECT
15	class_id,
16	COUNT(*)
17FROM
18	students
19GROUP BY
20	class_id
21HAVING
22	COUNT(*) != 4;
23
24-- 生徒数が4人より多いクラスを抽出します
25SELECT
26	class_id,
27	COUNT(*)
28FROM
29	students
30GROUP BY
31	class_id
32HAVING
33	COUNT(*) > 4;
34
35-- 生徒数が4人より少ないクラスを抽出します
36SELECT
37	class_id,
38	COUNT(*)
39FROM
40	students
41GROUP BY
42	class_id
43HAVING
44	COUNT(*) < 4;
45
46-- 生徒数が4人以上のクラスを抽出します
47SELECT
48	class_id,
49	COUNT(*)
50FROM
51	students
52GROUP BY
53	class_id
54HAVING
55	COUNT(*) >= 4;
56
57-- 生徒数が4人以下のクラスを抽出します
58SELECT
59	class_id,
60	COUNT(*)
61FROM
62	students
63GROUP BY
64	class_id
65HAVING
66	COUNT(*) <= 4;

sql/chapter07/02.sql

論理演算子(AND, OR, NOT)を使うと、複数の条件を組み合わせて、より複雑な絞り込みができます。

  • AND: 全ての条件を満たす場合にデータを抽出します。
  • OR: いずれかの条件を満たす場合にデータを抽出します。
  • NOT: 条件を否定します。
1-- 論理演算子(AND, OR, NOT)
2-- 生徒数が3人以上「かつ」idの平均が10より大きいクラスを抽出します
3SELECT
4	class_id,
5	COUNT(*) AS cnt,
6	AVG(id) AS avg_id
7FROM
8	students
9GROUP BY
10	class_id
11HAVING
12	COUNT(*) >= 3
13	AND AVG(id) > 10;
14
15-- 生徒数が3人「または」5人のクラスを抽出します
16SELECT
17	class_id,
18	COUNT(*) AS cnt
19FROM
20	students
21GROUP BY
22	class_id
23HAVING
24	COUNT(*) = 3
25	OR COUNT(*) = 5;
26
27-- 生徒数が3人ではないクラスを抽出します
28SELECT
29	class_id,
30	COUNT(*) AS cnt
31FROM
32	students
33GROUP BY
34	class_id
35HAVING
36	NOT COUNT(*) = 3;

sql/chapter07/03.sql

IS NULLIS NOT NULLは、集計結果がNULL(データが存在しない状態)であるか、そうでないかを判定するために使用します。COUNTのような集計関数は通常NULLを返しませんが、構文として覚えておくと便利です。

1-- NULL判定(IS NULL / IS NOT NULL)
2-- scoreをカウントした結果がNULLであるグループを抽出します
3SELECT
4	class_id,
5	COUNT(score) AS score_count
6FROM
7	students
8GROUP BY
9	class_id
10HAVING
11	COUNT(score) IS NULL;
12
13-- scoreをカウントした結果がNULLではないグループを抽出します
14SELECT
15	class_id,
16	COUNT(score) AS score_count
17FROM
18	students
19GROUP BY
20	class_id
21HAVING
22	COUNT(score) IS NOT NULL;

sql/chapter07/04.sql

LIKE演算子は、集計結果の文字列が特定のパターンに一致するかどうかを判定します。%は「0文字以上の任意の文字列」を表すワイルドカードです。

  • LIKE '山田': 完全一致
  • LIKE '山%': 後方一致(「山」で始まる)
  • LIKE '%田': 前方一致(「田」で終わる)
  • LIKE '%田%': 部分一致(「田」を含む)
1-- 文字列検索(LIKE)
2-- 各クラスで最も大きい(辞書順で後ろの)姓が「山田」であるグループを抽出します
3SELECT
4	class_id,
5	MAX(last_name)
6FROM
7	students
8GROUP BY
9	class_id
10HAVING
11	MAX(last_name) LIKE '山田';
12
13-- 各クラスで最も大きい姓が「山」で始まるグループを抽出します
14SELECT
15	class_id,
16	MAX(last_name) AS max_name
17FROM
18	students
19GROUP BY
20	class_id
21HAVING
22	MAX(last_name) LIKE '山%';
23
24-- 各クラスで最も大きい姓が「田」で終わるグループを抽出します
25SELECT
26	class_id,
27	MAX(last_name)
28FROM
29	students
30GROUP BY
31	class_id
32HAVING
33	MAX(last_name) LIKE '%田';
34
35-- 各クラスで最も大きい姓が「田」を含むグループを抽出します
36SELECT
37	class_id,
38	MAX(last_name)
39FROM
40	students
41GROUP BY
42	class_id
43HAVING
44	MAX(last_name) LIKE '%田%';

sql/chapter07/05.sql

IN演算子は、集計結果が指定した複数の値のいずれかに一致する場合にデータを抽出します。NOT INはその逆で、指定したどの値とも一致しない場合にデータを抽出します。

ORを複数つなげるよりも、シンプルに記述できます。

1-- 複数候補を指定(IN, NOT IN)
2-- 生徒数が3人または5人のクラスを抽出します
3SELECT
4	class_id,
5	COUNT(*) AS cnt
6FROM
7	students
8GROUP BY
9	class_id
10HAVING
11	COUNT(*) IN (3, 5);
12
13-- 生徒数が3人でも5人でもないクラスを抽出します
14SELECT
15	class_id,
16	COUNT(*) AS cnt
17FROM
18	students
19GROUP BY
20	class_id
21HAVING
22	COUNT(*) NOT IN (3, 5);

sql/chapter07/06.sql

BETWEEN演算子は、集計結果が指定した2つの値の範囲内(両端の値を含む)にあるかどうかを判定します。

BETWEEN A AND Bは、>= A AND <= Bと同じ意味になります。

1-- 範囲検索(BETWEEN)
2-- idの平均値が5以上かつ10以下のクラスを抽出します
3SELECT
4	class_id,
5	AVG(id) AS avg_id
6FROM
7	students
8GROUP BY
9	class_id
10HAVING
11	AVG(id) BETWEEN 5
12	AND 10;
13
14-- idの平均値が5から10の範囲外であるクラスを抽出します
15SELECT
16	class_id,
17	AVG(id) AS avg_id
18FROM
19	students
20GROUP BY
21	class_id
22HAVING
23	AVG(id) NOT BETWEEN 5
24	AND 10;

sql/chapter07/07.sql

WHERE句とHAVING句はどちらもデータを絞り込むための条件を指定しますが、役割が明確に異なります。

  • WHERE句: GROUP BYでデータをグループ化するに、元のテーブルの各行に対して条件を適用します。
  • HAVING句: GROUP BYでデータをグループ化したに、集計された結果に対して条件を適用します。

SQLの実行順序は、WHEREGROUP BYHAVING の順です。この違いを理解することが非常に重要です。

以下の例では、まずWHERE句でclass_idNULLでない行に絞り込み、その後GROUP BYでクラスごとにグループ化し、最後にHAVING句で生徒数が4人のクラスだけを抽出しています。

1-- WHEREとHAVINGの使い分け
2-- WHEREはグループ前、HAVINGはグループ後
3SELECT
4	class_id,
5	count(*)
6FROM
7	students
8WHERE
9	class_id IS NOT NULL
10GROUP BY
11	class_id
12HAVING
13	count(*) = 4
14ORDER BY
15	class_id;

おわりに

今回は、GROUP BYでグループ化した後の集計結果に、さらに条件を指定して絞り込むHAVING句について解説しました。最も重要なポイントは、グループ化する「前」のデータを絞るWHERE句とは処理のタイミングが異なるという点です。この違いにより、HAVING句ではCOUNT()AVG()といった集計関数を条件式に利用できます。記事で紹介した様々な演算子を使いこなすことで、より複雑な条件でのデータ抽出も可能になります。

【SQL】HAVING句の書き方と使い方の基本 | いっしー@Webエンジニア