コマンド道場

GROUP BY 句の使い方

ひとことで言うと

shain社員

busho_idsalary
1980000
2720000
2530000
2520000

GROUP BYまとめた結果

busho_idavg(salary)
1980000
2553763
3532750

GROUP BY busho_id

  • 社員 16 が、6にまとまります(同じ busho_id の行が 1 行になる)。
  • avg(salary)まとまりごとに計算した値で、元の表には無い列です。

この図では、部署が決まっていない社員を除いて数えています(NULL のまとまりについては後半で扱います)。

GROUP BY は「同じ値の行をひとまとめにして、まとまりごとに 1 行を返す」書き方です。 まとめた後は行が減るので、個々の行の情報は取り出せなくなります。ここが最初のつまずきどころです。

主要 7 製品で通用 そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite / SQL Server / Oracle Database / IBM Db2

製品ごとの対応表(実測)を見る

このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。

01「まとめる単位」を指定する

結論: `GROUP BY 列` と書くと、その列の値が同じ行がひとまとめになります。

返る行数は「まとまりの数」です。社員を部署ごとにまとめれば、返るのは部署の数だけ。何十人いても、部署が 6 つなら 6 行です。

まとまりごとに何を計算するかは SELECT に書きます。人数なら count(*)、平均給与なら avg(salary) です。

1 行ずつ、何をしているか
  1. SELECT busho_id, count(*), avg(salary)まとまりごとに計算する
  2. FROM shainどの表を見るか
  3. WHERE taishoku_on IS NULLまとめる前に行を絞る
  4. GROUP BY busho_id何を単位にまとめるか
  5. HAVING count(*) >= 2まとめた後に絞る
  6. ORDER BY busho_id並び順

WHERE は「まとめる前」、HAVING は「まとめた後」。この順番を押さえると使い分けで迷いません。

部署ごとの人数
SELECT busho_id, count(*) AS ninzu
FROM shain
GROUP BY busho_id
ORDER BY busho_id;
busho_idninzu
11
24
34
43
52
72
NULL2

7

02⚠️ よくある間違い:SELECT に書ける列は決まっている

`GROUP BY` に書いた列と、集約関数だけSELECT に書けます。

それ以外の列を書くとエラーになります。理由は単純で、まとまりの中で 1 つに決まらないからです。開発部に 4 人いるとき、「その部署の name」は 4 つあって 1 つに決められません。

必要なら、その列も GROUP BY に足すか、max() などで 1 つに決めます。

わざとエラーになる例
SELECT busho_id, name, count(*)
FROM shain
GROUP BY busho_id;
ERROR: column "shain.name" must appear in the GROUP BY clause or be used in an aggregate function

「name は GROUP BY にも集約関数にも入っていない」と言われます。エラーメッセージが原因をそのまま説明しています。

03⚠️ よくある間違い:count(*) と count(列) は別物

count(*)行の数を数えます。count(列)その列が NULL でない行の数を数えます。

値が入っていない行があると数が変わります。「人数」を数えたいのか「値が入っている行数」を数えたいのかで、使い分けてください。

この違いは外部結合と組み合わせたときに特に効きます(相手がいない行を 1 と数えてしまう)。

全員の数と、退職日が入っている人の数
SELECT count(*) AS zenin, count(taishoku_on) AS taishokusha
FROM shain;
zenintaishokusha
182

1

同じ表でも数が違います。count(taishoku_on) は NULL を数えていません。

04絞り込みは「まとめる前」か「まとめた後」か

まとめる前に行を減らすのが `WHERE`、まとめた後に減らすのが `HAVING` です。

「在籍者だけで平均を出す」なら、まとめる前に退職者を外すので WHERE です。「2 人以上いる部署だけ出す」なら、人数はまとめないと分からないので HAVING です。

両方書けます。順番も上のとおり WHEREGROUP BYHAVING です。

在籍者だけで集計し、2 人以上の部署に絞る
SELECT busho_id, count(*) AS ninzu, avg(salary)::numeric(10,0) AS heikin
FROM shain
WHERE taishoku_on IS NULL
GROUP BY busho_id
HAVING count(*) >= 2
ORDER BY busho_id;
busho_idninzuheikin
24553763
33550333
42552500
52539000
72451500
NULL2416000

6

05NULL も 1 つのまとまりになる

GROUP BY した列が NULL の行は、「NULL というまとまり」として 1 行にまとまります。捨てられるわけではありません。

WHERE では NULL の行が落ちるのに、GROUP BY では残ります。挙動が逆なので混乱しやすいところです。

出したくない場合は WHERE 列 IS NOT NULL で先に外します。

部署が未設定の社員も 1 つのまとまりになる
SELECT busho_id, count(*) AS ninzu
FROM shain
GROUP BY busho_id
ORDER BY busho_id NULLS LAST;
busho_idninzu
11
24
34
43
52
72
NULL2

7

busho_id が空の行が 1 行あります。これが「部署が決まっていない社員」のまとまりです。

⚠️ この書き方は MySQL / MariaDB / SQL Server では使えません(ORDER BY ... NULLS LAST)。製品ごとの対応表

06並び替えたいときは ORDER BY を別に書く

GROUP BYまとめるだけで、並び順は保証しません。「たまたま昇順に見える」ことはありますが、当てにしてはいけません。

並び順が必要なら ORDER BY を必ず書きます。集約した値でも並べられます。

SELECT で付けた別名は、ORDER BY では使えます(WHERE では使えません)。評価される順番が SELECTORDER BY だからです。

人数の多い順に並べる(別名で並べ替え)
SELECT busho_id, count(*) AS ninzu
FROM shain
GROUP BY busho_id
ORDER BY ninzu DESC, busho_id;
busho_idninzu
24
34
43
52
72
NULL2
11

7

自分で打ってみる

このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。

押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。

解いてみる

読んだだけでは書けるようになりません。実行結果で採点します。

関連するトピック

  • 集約関数(練習問題)まとまりごとに何を計算するか(COUNT / SUM / AVG / MIN / MAX)。
  • HAVING 句まとめた結果に条件を付けます。
  • WHERE 句まとめる前に行を絞ります。順番の違いが使い分けの決め手です。

根拠(一次情報)