SQLのwhere句とhaving句の違い、評価順序が原因で出力されるエラーと原因についてまとめています。


SQLのwhereとhavingの違い
where句もhaving句も絞り込み条件を指定するためのSQLです。以下のように単純な条件指定の場合ですと、結果は全く同じになります。以下は、MySQLで実行しています。
- mysql> select * from employees where emp_no=10001;
- +--------+------------+------------+-----------+--------+------------+
- | emp_no | birth_date | first_name | last_name | gender | hire_date |
- +--------+------------+------------+-----------+--------+------------+
- | 10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 |
- +--------+------------+------------+-----------+--------+------------+
- 1 row in set (0.00 sec)
- mysql> select * from employees having emp_no=10001;
- +--------+------------+------------+-----------+--------+------------+
- | emp_no | birth_date | first_name | last_name | gender | hire_date |
- +--------+------------+------------+-----------+--------+------------+
- | 10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 |
- +--------+------------+------------+-----------+--------+------------+
- 1 row in set (0.09 sec)
ただし、一般的にwhereよりもhavingのほうがレスポンスが遅くなります。特に理由がなければ、絞り込み条件はwhere句で指定するのが良いでしょう。
【関連記事】
▶SQL having 集約関数の絞り込み whereよりもレスポンスが遅い理由とは?
havingは、集計関数の絞り込み条件指定が可能
havingは、sum、max、avg、countなどの集計関数に対する絞り込み条件を指定することが可能です。
以下は、部署ごとの平均年収のうち、平均年収が7万ドル以上の部署のみを抽出するSQLです。
- SELECT
- departments.dept_name as bushomei,
- avg(salary) as avg_kyuuryo
- FROM
- salaries
- left join dept_emp ON dept_emp.emp_no = salaries.emp_no
- left join departments ON departments.dept_no = dept_emp.dept_no
- WHERE
- salaries.to_date="9999-01-01"
- group by
- bushomei
- having
- avg_kyuuryo > 70000
- +-----------+-------------+
- | bushomei | avg_kyuuryo |
- +-----------+-------------+
- | Sales | 88842.1590 |
- | Marketing | 80014.6861 |
- | Finance | 78644.9069 |
- +-----------+-------------+
- 3 rows in set (3.45 sec)
where句で指定している salaries.to_date=”9999-01-01″ は、最新の年収金額を抽出するための条件です。whereとは別に、平均年収(avg_kyuuryo)が70000以上という条件をhaving句で指定しています。
whereとhavingの評価順序
実は、whereとhavingは評価順序が異なります。以下の順番で評価されるんですね。
- where
- group by
- having
SQLのavg関数は、group byの評価が終わって始めて値が抽出されます。そのため、whereでavg関数の値を条件に指定することはできないんですね。group byの評価が完了したあとのhaving句にはavgの値を指定できます。
実際に、whereとhavingの2箇所で指定した条件を、whereに指定して試してみましょう。havingの条件をwhereに追記すると、以下のエラーが出力されました。
- SELECT
- departments.dept_name as bushomei,
- avg(salary) as avg_kyuuryo
- FROM
- salaries
- left join dept_emp ON dept_emp.emp_no = salaries.emp_no
- left join departments ON departments.dept_no = dept_emp.dept_no
- WHERE
- salaries.to_date="9999-01-01"
- and avg_kyuuryo > 70000
- group by
- bushomei;
- ERROR 1054 (42S22): Unknown column 'avg_kyuuryo' in 'where clause'
havingではselectの列のエイリアス、avg_kyuuryoが使えたのにwhereでは使えないようで、avg_kyuuryoは不明なカラム名だというエラーになりました。
実は一般的なDBMSでは、selectでasを使って別名定義した名前はwhere句やhaving句では使えません。MySQLの拡張で、havingでのみ別名定義が使えるようになっているんですね。
それでは、別名ではなくavg関数をそのまま条件式に指定するとどうなるのでしょう?
and avg_kyuuryo > 70000の箇所をavg(salary) > 70000 に書き換えると、また別のエラーが出力されました。
- ERROR 1111 (HY000): Invalid use of group function
グループ関数(集計関数)の使い方が間違っている、というエラーです。エラーメッセージからは直感的にわかりづらいですが、where句で集計関数は使えないというふうに考えて良いと思います。
まとめ

- where句とhaving句はどちらも絞り込み条件を指定できる
- having句は、avg、max、sum、countなどの集計関数の絞り込み条件が指定可能
- 一般的にhaving句はレスポンスが遅くなるので、集計関数以外の条件はwhereで指定する