Q1GROUP BY name を指定したとき、結果の行数は何になりますか(score テーブル / 氏名は 10 種類)。
GROUP BY と HAVING — グループ集計
SQL の GROUP BY と HAVING を解説します。グループごとの集計、HAVING による絞り込み、WHERE との違いを CSV の成績データで実行しながら学べます。
本記事で使うデータ — score テーブル
前の記事ではテーブル全体を 1 つの値にまとめました。本記事の `GROUP BY`(グループ化。同じ値を持つ行を 1 つのグループにまとめる句)は、集計を グループ単位 で行い、グループの数だけ集計行を返します。さらに `HAVING` で「平均が 80 以上のグループだけ」のように 集計結果を絞り込み ます。
題材は前回と同じ score テーブル(30 行 = 10 名 × 3 科目)。name で 10 グループ、subject で 3 グループになります。
GROUP BY — グループごとに集計する
GROUP BY 列 を書くと、その列の値が同じ行が 1 つのグループにまとめられ、集計関数は グループごとに 1 回ずつ 計算されます。SELECT name, AVG(score) FROM score GROUP BY name; なら、氏名が同じ 3 行(3 科目分)が 1 グループになり、氏名ごとに 1 行ずつ平均が返ります。SELECT に書ける「素の列」は `GROUP BY` に指定した列 だけで、それ以外の列は集計関数で包む必要があります(グループ内で値が 1 つに定まらないため)。
AVG(score) がグループごとに計算されます。10 名なので結果は 10 行になります。-- 科目ごとの 件数 / 平均 / 最高点 (subject で 3 グループ)
SELECT
subject,
COUNT(*) AS row_count,
ROUND(AVG(score), 1) AS avg_score,
MAX(score) AS top_score
FROM score
GROUP BY subject;
HAVING — 集計結果を絞り込む
グループ集計の後で「平均が 80 以上のグループだけ見たい」のように 集計値で絞り込みたい ことがあります。これは WHERE ではできません。WHERE はグループ化の前に個々の行を判定する句なので、AVG(score) のようなグループ集計値をまだ計算していないからです。そこで使うのが `HAVING` です。HAVING は GROUP BY の後ろに書き、グループ集計の結果に対して 条件を判定します。HAVING AVG(score) >= 80 と書けば、平均 80 以上のグループだけが残ります。
GROUP BY で 10 グループに集計してから、HAVING AVG(score) >= 80 が 集計済みのグループ を判定し、条件を満たすグループだけを残します。-- 平均が 85 を超える氏名だけ (HAVING で集計値を判定)
SELECT
name,
ROUND(AVG(score), 2) AS avg_score
FROM score
GROUP BY name
HAVING AVG(score) > 85;
WHERE と HAVING の違い — 行を絞るか、グループを絞るか
WHERE と HAVING はどちらも絞り込みですが、働くタイミングが違います。WHERE は グループ化する前 に、個々の行を判定して落とします。HAVING は グループ化して集計した後 に、グループ単位で判定して落とします。実行順序は FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY です。
両方を同時に使うこともでき、その場合は「WHERE で対象行を絞ってからグループ化し、HAVING で集計結果を絞る」という流れになります。たとえば「Math と English だけを対象に、氏名ごとの平均が 80 以上の人」を出すなら、WHERE subject IN ('Math','English') で行を絞り、HAVING AVG(score) >= 80 でグループを絞ります。
WHERE は グループ化の前 に素の列で行を判定して落とし、HAVING は グループ化の後 に集計値(AVG など)でグループを判定して落とします。脱落する場所が違うのが要点です。例: WHERE subject='Math'(行を絞る)と HAVING AVG(score)>=80(グループを絞る)。-- WHERE と HAVING を併用する
-- Science を除外し、平均が 80 以上の氏名だけ残す
SELECT
name,
ROUND(AVG(score), 2) AS avg_two
FROM score
WHERE subject <> 'Science'
GROUP BY name
HAVING AVG(score) >= 80;
理解度チェック
まずは1問ずつ答えてみましょう。
Q2「氏名ごとの平均点が 80 以上の人だけ」を抽出したいとき、80 以上の条件はどの句に書きますか。
Q3WHERE と HAVING の違いの説明として正しいものはどれですか。