「平均給料が35万円を超える役割だけを見たい」——これはWHEREで書けるでしょうか? それともGROUP BYのあとに、また別の何かが必要でしょうか?
前回のGROUP BYは、行をグループにまとめて集計する道具でした。では、できあがったグループそのものを条件で絞り込みたいときは、どうすればいいでしょう。少し考えてから読み進めてください。
WHEREとHAVINGの違い
結論から言うと、WHEREは「行」を絞り込み、HAVINGは「グループ」を絞り込みます。
crewテーブルで確認しましょう。トウマとリョウは配属先が決まっておらずhomeがNULLです。
crew テーブル:
| id | name | role | age | home | salary |
|---|---|---|---|---|---|
| 1 | ユミ | エンジニア | 24 | 地球 | 320000 |
| 2 | ケンジ | パイロット | 35 | 火星 | 400000 |
| 3 | サクラ | 医師 | 41 | 地球 | 520000 |
| 4 | ハルト | 医師 | 33 | 月 | 450000 |
| 5 | レイ | 研究員 | 29 | 火星 | 380000 |
| 6 | ミドリ | パイロット | 27 | 地球 | 360000 |
| 7 | ソラ | エンジニア | 45 | 月 | 470000 |
| 8 | トウマ | 研究員 | 24 | NULL | 270000 |
| 9 | アカリ | エンジニア | 31 | 火星 | 350000 |
| 10 | リョウ | パイロット | 38 | NULL | 440000 |
役割ごとの平均給料は、次のようになります(前回計算したとおりです)。
| 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はもう怖くないよ!