【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 NULLやIS 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の実行順序は、WHERE → GROUP BY → HAVING の順です。この違いを理解することが非常に重要です。
以下の例では、まずWHERE句でclass_idがNULLでない行に絞り込み、その後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()といった集計関数を条件式に利用できます。記事で紹介した様々な演算子を使いこなすことで、より複雑な条件でのデータ抽出も可能になります。