GROUP BY句は、抽出された行を、指定された列の値が同じ行ごとにグループ化するために使用されます。行をグループ化するだけでは意味がないので、通常、集約関数と一緒に使われます。

これまで見てきた集約関数の使い方は、NULLを含む・含まない、重複行を除外する・除外しないとう区別はあるにせよ、とにかくテーブル全体を集約範囲としてきました。GROUP BY句を用いることで今度は、テーブルをいくつかのグループに切り分けて集約することができます。

わかりやすい表現として、日本語ではよく「~ごと」や「~単位」というように何かの単位でグループ化します。GROUP BY句と集約関数を使用することで「職種IDごと」や「商品分類ごと」などのようにグループ分けして集約することができます。

GROUP BY句の構文
SELECT 列名1, 列名2, 列名3, …
FROM テーブル名
GROUP BY 列名1, 列名2, 列名3, …

下記例では職種ID(job_id)ごとの行数を数えています。

SELECT job_id, COUNT(*)
  FROM employees
 GROUP BY job_id;
実行結果
  job_id  | count
----------+-------
 ST_CLERK |     1
 IT_PROG  |     3
 AD_PRES  |     1
 ST_TRAIN |     1
(4 行)

このようにGROUP BY句なしではテーブル全体をひとつのグループと見なしていたのに対し、GROUP BY句を使うことで複数のグループに分けることができます。

GROUP BY句に指定する列のことを集約キーやグループ化列と言います。SELECT句と同じく、GROUP BY句にも複数の列をカンマ区切りで指定することができます。

GROUP BY句の使用ルール

GROUP BY句は位置にも厳密なルールがあり、必ずFROM句の後ろ、WHERE句を指定するならさらにその後ろに記述する必要があります。この順番を守らないとSQLは正しく動作せず、エラーになります。

また、SELECT句で指定する抽出項目は以下である必要があります。

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

この3つのいずれかでなければなりません。
例えば、以下のSQLは実行してもエラーとなります。

SELECT name, job_id, COUNT(*)
  FROM employees
 GROUP BY job_id;
ERROR:  列"employees.name"はGROUP BY句で出現しなければならないか、集約関数内で使用しなければなりません
行 1: select name, job_id, count(*)

name列をGROUP BY句で指定していないため、このSQLは間違いです。

ではなぜ上記のような構文が許されないのでしょう。GROUP BY句によって何らかのキーでグループ化したということは、結果に出てくる1行あたりの単位もそのグループになっている、ということです。上記の例で例えると、職種ID(job_id)でグループ化すれば、1行につき1つの職種IDが表れます。ここまでは良いのですが、ここにname列を追加した場合に問題となります。職種IDという集約キーと社員名(name)が必ずしも一対一に対応しないという点です。

集約キーに対して複数の値が存在する列をSELECT句に含めることは論理的に不可能というわけです。

もうひとつ注意しないければならない点として、GROUP BY句には列の別名を指定できません。SELECT句でASキーワードを使うと表示用の別名をつけることができますが、GROUP BY句ではこの別名を使うことができないのです。
その理由は、DBMS内部でSQL文が実行される順序にあります。SELECT句はGROUP BY句よりも後で実行されます。そのため、GROUP BY句の時点ではSELECT句をつけた別名をDBMSはまだ知らないのです。
(実は、PostgreSQLは列の別名を指定してもエラーとならず、無事に実行されます。ただ、この書き方は他のDBMSでは使用できないことが多いので使えないものとして覚えておいたほうが良いでしょう。)

NULLの扱い

集約キーにNULLが含まれていた場合はどうなるでしょう。それを確かめるために以下のSQLを実行してみます。

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

実行結果からもわかるように集約キーにNULLが含まれている場合、「NULL」というひとつのに分類されます。

WHERE句とGROUP BY句の併用

WHERE句を使った場合のGROUP BY句の動作について見ていきます。まず最初の確認ポイントになりますが、GROUP BY句を使用するSELECT文においても、WHERE句は問題なく使用できます。

GROUP BY句の構文
SELECT 列名1, 列名2, 列名3, …
FROM テーブル名
WHERE 条件式
GROUP BY 列名1, 列名2, 列名3, …

WHERE句を指定して集約を行う場合、WHERE句で指定した条件で先にレコードが絞り込まれてから集約が行われます。

SELECT department_id, COUNT(*)
  FROM employees
 WHERE hire_date > '2000-1-1'
 GROUP BY department_id;
実行結果
 department_id | count
---------------+-------
               |     2
 0100          |     1
 0050          |     2
(3 行)

このSELECT文の実行結果より、まずWHERE句で入社日(hire_date)が2000/1/1より後になるレコードに絞り込まれてから、GROUP BY句により集約されていることがわかります。