STAGE 04 — 4-3 / 集計とグループ化

HAVINGでグループを絞り込む

考えてみよう

「平均給料が35万円を超える役割だけを見たい」——これはWHEREで書けるでしょうか? それともGROUP BYのあとに、また別の何かが必要でしょうか?

前回のGROUP BYは、行をグループにまとめて集計する道具でした。では、できあがったグループそのものを条件で絞り込みたいときは、どうすればいいでしょう。少し考えてから読み進めてください。

WHEREとHAVINGの違い

結論から言うと、WHEREは「行」を絞り込み、HAVINGは「グループ」を絞り込みます

crewテーブルで確認しましょう。トウマとリョウは配属先が決まっておらずhomeがNULLです。

crew テーブル:

idnameroleagehomesalary
1ユミエンジニア24地球320000
2ケンジパイロット35火星400000
3サクラ医師41地球520000
4ハルト医師33450000
5レイ研究員29火星380000
6ミドリパイロット27地球360000
7ソラエンジニア45470000
8トウマ研究員24NULL270000
9アカリエンジニア31火星350000
10リョウパイロット38NULL440000

役割ごとの平均給料は、次のようになります(前回計算したとおりです)。

role平均給料
エンジニア380000
パイロット400000
医師485000
研究員325000

ここから「平均給料が35万円を超える役割だけ」を残したいとします。まだグループ化する前の1行1行には「平均給料」という値は存在しないので、WHEREでは書けません。そこで使うのがHAVINGです。

SELECT role, AVG(salary) AS 平均給料
FROM crew
GROUP BY role
HAVING AVG(salary) > 350000;

結果:

role平均給料
エンジニア380000
パイロット400000
医師485000

研究員グループは平均給料が325000円で条件を満たさないため、結果から除かれています。HAVINGは、GROUP BYで作られたグループの集計値に対して条件をかける句なのです。

WHEREとHAVINGを組み合わせる

WHEREとHAVINGは、同じSQLの中で両方使うこともできます。「月出身の乗員を除いたうえで、人数が2人以上いる役割だけを見たい」という例を考えてみましょう。

SELECT role, COUNT(*) AS 人数
FROM crew
WHERE home != '月'
GROUP BY role
HAVING COUNT(*) >= 2;

結果:

role人数
エンジニア2
パイロット2

WHERE home != '月'は、グループ化するに月出身のハルトとソラを取り除きます。また、homeがNULLのトウマとリョウも、NULLとの比較は真にならないため同様に除かれます(IS NULLとの違いは前のSTAGEで学んだとおりです)。そのあと残った6人をroleでグループ化し、最後にHAVING COUNT(*) >= 2で「2人以上いるグループだけ」を残しています。医師と研究員はそれぞれ1人しか残らないため、除かれました。

実行順序のイメージ

WHEREとHAVINGを混同しないコツは、SQLが実際に処理を進める実行順序をイメージすることです。書く順序と、処理される順序は違います。

SELECT role, COUNT(*)     -- ⑤ 最後に列を選び出す
FROM crew                 -- ① まずテーブルを決める
WHERE home != '月'        -- ② 行を絞り込む(グループ化の前)
GROUP BY role              -- ③ 行をグループにまとめる
HAVING COUNT(*) >= 2      -- ④ グループを絞り込む(集計のあと)
ORDER BY role;              -- ⑥ 並べ替え

実行順序は「FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT」です。WHEREはグループ化される前の生の行に対する条件、HAVINGはグループ化されたあとの集計結果に対する条件——この順番をイメージできれば、「なぜWHEREでは集計関数が使えないのか」「なぜHAVINGはGROUP BYとセットで使うのか」が、自然と腑に落ちるはずです。

確認問題

「給料が35万円を超える乗員だけを対象に、役割ごとの人数を数えたい」場合、salary > 350000という条件は、WHEREとHAVINGのどちらに書くべきでしょうか?

答えを見る

正解はWHEREです。salaryは1人ひとりの給料であり、グループ化される前の「行」に対する条件だからです。WHERE salary > 350000で先に対象者を絞り込んでからGROUP BY roleでまとめるのが正しい順序です。HAVINGは、COUNT(*)AVG(salary)のような、グループ化したあとの集計結果に対する条件にのみ使います。

これでSTAGE 04「集計とグループ化」は完了です。WHEREで行を絞り込み、GROUP BYでまとめ、HAVINGでグループを絞り込む——この3つの流れを押さえれば、集計SQLの基本形はマスターしたも同然です。次のSTAGE 05からは、複数のテーブルを扱う考え方に進みます。

/sql/game/ の SQL DIVER で、WHERE・GROUP BY・HAVINGを組み合わせたSQLを実際にタイプして体になじませておきましょう。

ゆみちゃん
ゆみ

WHEREは行のふるい、HAVINGはグループのふるい。実行順序は「FROM→WHERE→GROUP BY→HAVING→SELECT」——ここまで来たら、集計SQLはもう怖くないよ!