まだ話してなかったことがあるんだ。それは集約した後のグループをどうやってフィルタリングするか?ってこと。全部の学部が欲しいわけじゃなくて、例えば学生が100人以上いる学部だけ知りたいとか、平均給料が50,000より高い部署だけ見たいとか。今日はHAVINGを使って集約データをフィルタリングする方法を紹介するよ。
HAVINGってなんで必要なの?WHEREがあるじゃん?GROUP BYの後にWHERE置けばいいんじゃない? :)
そんなに単純じゃないんだ!まず、SQLの構文の順番は決まってて、WHEREはGROUP BYより前に実行されるんだよ。
じゃあ、GROUP BYの後にWHEREを置けないの?
それもダメ!よくあるのは、まずテーブルの行をフィルタしてからグループ化して、その後でグループごとにいらないデータを捨てたいってパターン。
じゃあ、WHEREをコピーして、名前をHAVINGにしてGROUP BYの後に置けばいいんじゃない?
そうそう、それでOK! :)
HAVINGとWHEREの違い
WHEREはグループ化する前の行をフィルタする。
例えば、ケーキを味で選ぶとき:いちご味とチョコ味だけ残して、他はスルー。これがWHEREの役割。
HAVINGはデータをグループ化して集約関数が動いた後でフィルタする。
例えば、もうケーキをテーブルごとにグループ化して数を数えた後、ケーキが3個以上あるテーブルだけ残したいとき。
つまり、HAVINGはグループ単位でデータをフィルタするために使うんだ。
HAVINGの構文
構文はほぼWHEREと同じだけど、動きがちょっと違う:
SELECT カラム, 集約関数
FROM テーブル
GROUP BY カラム
HAVING 条件;
実行の流れ:
- まず
WHEREで行をフィルタする。 - 次に
GROUP BYでデータをグループ化。 - グループ化した結果に集約関数を適用。
- 最後に
HAVINGでグループをフィルタする。
HAVINGの使い方例
例1: 学生数が多い学部をフィルタ
大学で学生が100人以上いる学部を知りたいとする。テーブルstudentsがあるとしよう:
| id | name | faculty |
|---|---|---|
| 1 | Alice | Engineering |
| 2 | Bob | Engineering |
| 3 | Charlie | Arts |
| 4 | Daisy | Business |
| 5 | ... | ... |
クエリ:
SELECT faculty, COUNT(*) AS student_count
FROM students
GROUP BY faculty
HAVING COUNT(*) > 100;
何が起きてるか:
- まず
GROUP BYでfacultyごとに学生をグループ化。 - 次に集約関数
COUNT(*)で各学部の学生数を数える。 - 最後に
HAVINGで学生が100人以下の学部を除外。
結果:
| faculty | student_count |
|---|---|
| Engineering | 150 |
| Arts | 120 |
例2: 平均給料が高い部署
平均給料が50,000を超える部署だけ知りたいとき。テーブルemployeesがあるとする:
| id | name | department | salary |
|---|---|---|---|
| 1 | Alice | IT | 60000 |
| 2 | Bob | HR | 45000 |
| 3 | Charlie | IT | 70000 |
| 4 | Daisy | HR | 52000 |
| 5 | ... | ... | ... |
クエリ:
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000;
結果:
| department | avg_salary |
|---|---|
| IT | 65000 |
ポイント:HAVINGはGROUP BYの後で計算された結果に対して動くよ。
WHERE, GROUP BY, HAVINGの実行順序
WHEREとHAVINGのフィルタは違うタイミングで実行される。違いをもっと理解するために、クエリの流れを見てみよう:
WHERE: 行のフィルタ。この段階でテーブルの全行が処理される。
WHEREの条件に合わない行はこの時点で消える。GROUP BY: 行のグループ化。フィルタ後の行が
GROUP BYで指定したカラムごとにまとめられる。集約関数:
グループ化したデータに
COUNT()、AVG()、SUM()などの集約関数を適用。HAVING: グループのフィルタ。この段階では集約結果だけが処理される。
HAVINGの条件はグループにだけ適用される。
HAVINGの特徴
特徴1: 集約関数との連携
HAVINGとWHEREの一番の違いは、集約関数と一緒に使えること。例えば:
SELECT department, AVG(salary)
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000;
このクエリではAVG(salary)はWHEREの中では使えない。なぜならWHEREはグループ化前の行にしか使えないから。こんなクエリ:
SELECT department, AVG(salary)
FROM employees
WHERE AVG(salary) > 50000
GROUP BY department;
はエラーになる:aggregate functions are not allowed in WHERE。
特徴2: グループ化なしでも使える
HAVINGはGROUP BYがなくても使える。この場合、全レコードが1つのグループとして扱われる:
SELECT AVG(salary) AS avg_salary
FROM employees
HAVING AVG(salary) > 50000;
実践例
例えば、ショップがあって、売上テーブルsalesがあるとする:
| id | product_id | sales_amount |
|---|---|---|
| 1 | 101 | 200.00 |
| 2 | 102 | 300.00 |
| 3 | 101 | 400.00 |
| 4 | 103 | 150.00 |
クエリ:合計売上が500を超える商品を探す。
SELECT product_id, SUM(sales_amount) AS total_sales
FROM sales
GROUP BY product_id
HAVING SUM(sales_amount) > 500;
結果:
| product_id | total_sales |
|---|---|
| 101 | 600.00 |
よくあるミス
WHEREで集約関数を使う:
例えば:
SELECT department, AVG(salary)
FROM employees
WHERE AVG(salary) > 50000
GROUP BY department;
エラー:WHEREで集約関数は使えない。
NULLに関するミス:
データにNULLがあると、フィルタ結果が予想外になることがある。例えば:
SELECT department, SUM(salary)
FROM employees
GROUP BY department
HAVING SUM(salary) > 0;
salaryカラムが全部NULLだと、結果が0や空になることがある。
おめでとう!これで集約データのフィルタもバッチリできるようになったね!HAVINGはグループ単位の分析のカギだから、普通のWHEREじゃ足りないときはぜひ使ってみて!
GO TO FULL VERSION