はじめに
業務でSQLを読んでいたとき、次のようなコードを見かけました。
SELECT
customer_id,
MAX(status) AS status
FROM orders
WHERE status = 1
GROUP BY customer_id;
最初に思ったのは、
WHERE句で status = 1 に絞り込んでいるのだから、MAX(status) の結果も必ず1になるのでは?
ということでした。
この段階で調べていくうちに、そもそも「GROUP BY」が何をしているのか。を十分理解できていなかったことに気付きました。
この記事では、SQLにまだ慣れていない方向けに、
- GROUP BYとは何か
- なぜエラーになるのか
- なぜMAX()が必要なのか
を具体例を使いながら解説します。
まずはサンプルデータを見てみる
注文テーブル(orders)があるとします。
| customer_id | status |
|---|---|
| 1 | 完了 |
| 2 | 完了 |
| 2 | 発送中 |
| 2 | 完了 |
| 3 | 完了 |
| 3 | キャンセル |
customer_id は顧客ID、
status は注文状況です。
GROUP BYとは何か
次はSQLを見てみます。
SELECT customer_id
FROM orders
GROUP BY customer_id;
結果はこうなります。
| customer_id |
|---|
| 1 |
| 2 |
| 3 |
GROUP BYは、
同じ値を持つ行をグループとしてまとめる
ための機能です。
イメージとしては次のようになります。
customer_id = 1
└ 完了
customer_id = 2
├ 完了
├ 発送中
└ 完了
customer_id = 3
├ 完了
└ キャンセル
customer_idごとにグループが作られている状態です。
なぜstatusをそのままSELECTできないの?
では次にこのSQLを見てみます。
SELECT
customer_id,
status
FROM orders
GROUP BY customer_id;
初心者の頃の私は、
customer_idでまとめているだけだから、statusも表示できるのでは?
と思っていました。
しかし、このSQLはエラーになります。
SQLはどのstatusを返せばいいか分からない
例えば customer_id = 2 のグループを見てみます。
customer_id = 2
完了
発送中
完了
ここでSQLは困ります。
customer_idは分かった
でもstatusはどれを返せばいいの?
完了?
発送中?
最後の完了?
どれが正しいのか判断できません。
そのためSQLは
どの値を返せばいいか分からないのでエラーにします
という動きをします。
GROUP BYにはルールがある
GROUP BYを使う場合、
SELECT句には次のどちらかしか書けません。
① GROUP BYで指定した列
SELECT customer_id
FROM orders
GROUP BY customer_id;
② 集計関数で囲んだ列
SELECT
customer_id,
MAX(status)
FROM orders
GROUP BY customer_id;
代表的な集計関数には次のようなものがあります。
| 関数 | 意味 |
|---|---|
| MAX() | 最大値 |
| MIN() | 最小値 |
| SUM() | 合計 |
| AVG() | 平均 |
| COUNT() | 件数 |
MAX()を使うとエラーにならない理由
次のSQLを見てみます。
SELECT
customer_id,
MAX(score)
FROM scores
GROUP BY customer_id;
データが次のようになっていた場合、
| customer_id | score |
|---|---|
| 2 | 10 |
| 2 | 30 |
| 2 | 20 |
結果はこうなります。
| customer_id | MAX(score) |
|---|---|
| 2 | 30 |
今回はSQLに
一番大きい値を返してください
という明確な指示を出しています。
そのためSQLは迷わず結果を返せます。
では今回のSQLでは何が起きている?
今回のSQLはこちらでした。
SELECT
customer_id,
MAX(status)
FROM orders
WHERE status = 1
GROUP BY customer_id;
まずWHERE句が実行されます。
WHERE status = 1
によって、statusが1のデータだけが残ります。
例えば元データが
| customer_id | status |
|---|---|
| 1 | 1 |
| 1 | 1 |
| 2 | 1 |
| 2 | 1 |
だった場合、
グループは次のようになります。
customer_id = 1
1
1
customer_id = 2
1
1
この場合MAX()の結果は?
各グループには1しか存在しません。
そのため、
MAX(status)
を実行すると
1の中の最大値 → 1
になります。
結果は次の通りです。
| customer_id | status |
|---|---|
| 1 | 1 |
| 2 | 1 |
「じゃあMAX()って意味ないの?」
今回のケースでは、
計算結果だけを見るとほぼ意味がないように見えます。
なぜなら、
WHERE status = 1
によって、最初から1しか存在しないからです。
しかし、
SELECT
customer_id,
status
FROM orders
GROUP BY customer_id;
ではエラーになります。
そのため、
MAX(status)
を付けることで、
この列はちゃんと集計していますよ
とSQLに伝えている、、ということがわかりました
まとめ
今回学んだことを整理します。
- GROUP BYは同じ値を持つ行をまとめる機能
- GROUP BYした列以外は、そのままSELECTできない
- SQLは「どの値を返せばいいか分からない」とエラーになる
- MAX()などの集計関数を使うことで、返す値を明確に指定できる
- WHERE句で1に絞り込まれている場合、MAX(status)の結果も1になる
私は最初、
値が1しかないならMAX()は不要では?
と思っていました。
しかし実際は、
GROUP BYのルールを守るために書かれている
というケースもあることが分かりました。
同じ疑問を持った方の参考になれば幸いです。