HAVING句には、GROUP BY句によってグループ化された後の行に対する抽出条件を指定します。

WHERE句には集約関数は書けない

集約関数により、集約した後の結果に対して、抽出条件を指定したい場合があります。具体例を示すため、まず、部門ID(department_id)で集約して、行数を数えるSQLを実行します。

SELECT department_id, COUNT(*)
  FROM employees
 GROUP BY department_id;
実行結果
 department_id | count
---------------+-------
               |     2
 0100          |     1
 0010          |     1
 0050          |     2
(4 行)

この結果から2行のグループを抽出したいとします。抽出条件を示すのはWHERE句なので、WHERE句に以下のように書いてみます。

SELECT department_id, COUNT(*)
  FROM employees
 WHERE COUNT(*) = 2
 GROUP BY department_id;

しかし、このSELECT文はエラーとなり失敗します。

エラー内容のとおり、WHERE句にはCOUNTなどの集約関数を記述することはできません。集約関数を記述できるのはSELECT句、ORDER BY句、それからHAVING句のみです。

ここで出てきたHAVING句が今回やりたかった「2行のグループだけを選択する」というようなグループに対する条件を指定できる場所になります。

HAVING句

WHERE句はあくまで「レコード(行)」に対してしか条件を指定できません。COUNT関数などの集約関数を使ってデータをグループ化した場合、その結果に対する条件指定はHAVING句で行います。

HAVING句の構文
SELECT 列名1, 列名2, 列名3, …
 FROM テーブル名
 GROUP BY 列名1, 列名2, 列名3, …
 HAVING グループの値に対する条件

HAVING句を記述する位置は、GROUP BY句の後ろである必要があります。

SELECT文が実行する条件の順序は以下になります。

  1. FROM句に書かれた表を抽出する
  2. WHERE句に書かれた条件に適合する行を抽出する
  3. GROUP BY句に書かれた項目に従って行をグループ化する
  4. HAVING句に書かれた条件を満たすグループを抽出する
  5. ORDER BY句に書かれた条件で並び替えをする

上記した順序からわかるようにWHERE句の次にGROUP BY句の順で評価されるため、WHERE句はグループ化されたデータに対して絞込みを行うことができないことになります。

では、「2行のグループだけを選択する」SELECT文をHAVING句を使って記述します。

SELECT department_id, COUNT(*)
  FROM employees
 GROUP BY department_id;
HAVING COUNT(*) = 2

行数が2行のグループのみが抽出されました。

HAVING句の使用ルール

前述のとおり、HAVING句を記述する位置は、GROUP BY句の後ろでなければなりません。また、HAVING句の後に指定する抽出条件は以下である必要があります。

  • GROUP BY句の後に指定した列
  • 集約関数
  • 定数

この制限はGROUP BY句を使ったときのSELECT句とまったく同じです。

WHERE句とHAVING句の使い分け

HAVING句は前述のとおりグループ化した列に対する条件を指定する句です。ただ、集約キーとなる(GROUP BY句で指定した)列に対してはHAVING句に下記例のように条件指定することもできます。

SELECT department_id, COUNT(*)
  FROM employees
 GROUP BY department_id
HAVING department_id = '0100';

ただ、このSELECT文、以下のように書いても同じ結果を得られます。

SELECT department_id, COUNT(*)
  FROM employees
 WHERE department_id = '0100'
 GROUP BY department_id;

違いは、条件を記述する場所がWHERE句かHAVING句かだけで、実行結果はまったく同じです。

このような場合、どちらの句に条件を記述するのが良いのでしょう。

それを考える上で、各句の目的をまずおさらいします。WHERE句はレコード(行)に対する条件を指定する場所であり、HAVING句はグループに対する条件を指定する場所です。

この例の場合は、レコードに対する条件なので、WHERE句に記述するのが適切と言えます。

これは処理速度の面でも差がでます。一般的に、HAVING句よりもWHERE句で条件を記述した方が処理速度は速くなります。その理由のひとつとして、SELECT文が実行する条件の順序を振り返ってほしいのですが、WHERE句の方がHAVING句より先に実行されるため、先に条件を絞り込むことができるためです。