はじめに
SQLを書いていると、こんな疑問を持ったことはありませんか?
-
GROUP BYすると、データはどうまとめられるの? -
MAXとSUMは、それぞれ何をしているの? -
MAXで同じ最大値が複数あったらどうなるの? -
NULLが入っている場合、MAXやSUMの結果はどうなるの? -
NVLはなぜ使うの?
特に、
GROUP BY user_id
と
MAX(...)
SUM(...)
を組み合わせたSQLは、初学者にとって少し分かりづらい💦
そこで今回は、ECサイトの注文履歴を例にして、実際のデータがどのように集計されるのかを見ながら解説します。
この記事を読み終わるころには、
「GROUP BYでデータをグループ化して、MAXは最大値、SUMは合計値を返すんだ!」
というところまで理解できることを目指します。
この記事の対象読者
この記事は、以下のような方を対象にしています。
- SQLを勉強し始めたばかりの方
-
SELECTやWHEREは分かるけれど、GROUP BYがまだ苦手な方 -
MAXやSUMなどの集計関数を使ったSQLを理解したい方 -
NULLとNVLの関係を知りたい方 - 実際のデータを使ってSQLの動きを理解したい方
反対に、SQLの集計処理をすでに理解している方には、少し基本的な内容に感じるかもしれません。
1. まずは完成形のSQLを見てみよう
今回使用するSQLはこちらです。
SELECT
user_id
,MAX(NVL(shipping_fee, 0)) AS max_shipping_fee
,SUM(NVL(order_price, 0)) AS total_order_price
FROM
orders
GROUP BY
user_id;
一見すると、
MAX(NVL(shipping_fee, 0))
や
SUM(NVL(order_price, 0))
など、少し複雑に見えます。
しかし、1つずつ分解すると、それほど難しくありません。
このSQLでやっていることは、大きく分けて3つです。
-
user_idごとにデータをグループ化する - グループ内の送料から最大値を取得する
- グループ内の購入金額を合計する
まずは、この3つを順番に見ていきましょう。
2. GROUP BYとは?
まず重要なのが GROUP BY です。
今回のSQLでは、
GROUP BY user_id
としています。
これは簡単にいうと、
同じ
user_idのデータを1つのグループとしてまとめる
という処理です。
例えば、元データが以下のようになっていたとします。
集計前のデータ
| user_id | shipping_fee | order_price |
|---|---|---|
| 100 | 500 | 5,000 |
| 100 | NULL | 3,000 |
| 100 | 1,000 | 2,000 |
| 200 | 500 | 4,000 |
| 200 | 800 | 6,000 |
このデータに、
GROUP BY user_id
を指定すると、
user_id = 100 のグループ
user_id = 200 のグループ
というように、user_id ごとにグループが作られます。
イメージとしては、
100
├─ 500円
├─ NULL
└─ 1,000円
200
├─ 500円
└─ 800円
という状態です。
ここから、各グループに対して MAX や SUM などの集計関数を使います。
3. MAXとは?
MAX は、
指定したグループの中から最大の値を返す
集計関数です。
基本的な書き方は以下です。
SELECT MAX(カラム名)
FROM テーブル名;
例えば、
SELECT MAX(shipping_fee)
FROM orders;
とすると、shipping_fee の中で最も大きい値を取得できます。
4. SUMとは?
一方、SUM は、
指定したグループの値を合計する
集計関数です。
例えば、
SELECT SUM(order_price)
FROM orders;
とすると、order_price の合計値を取得できます。
つまり、
| 関数 | 何をする? |
|---|---|
MAX |
最大値を返す |
SUM |
合計値を返す |
と覚えておくと分かりやすいです。
5. GROUP BYとMAX・SUMを組み合わせるとどうなる?
ここまで分かったところで、最初のSQLをもう一度見てみましょう。
SELECT
user_id
,MAX(NVL(shipping_fee, 0)) AS max_shipping_fee
,SUM(NVL(order_price, 0)) AS total_order_price
FROM
orders
GROUP BY
user_id;
今回のポイントは、
GROUP BY user_id
でユーザーごとにグループを作り、そのグループに対して、
MAX(shipping_fee)
と
SUM(order_price)
を実行していることです。
6. 実際のデータで確認してみよう
例えば、user_id = 100 の注文データが3件あるとします。
| user_id | shipping_fee | order_price |
|---|---|---|
| 100 | 500 | 5,000 |
| 100 | NULL | 3,000 |
| 100 | 1,000 | 2,000 |
ここで、
GROUP BY user_id
を実行すると、3件のデータが「user_id = 100」という1つのグループとして扱われます。
そして、このグループに対して MAX と SUM を実行します。
MAXの場合
送料は、
500
NULL
1,000
です。
今回は NVL(shipping_fee, 0) を使っているので、
500
0
1,000
として扱われます。
この中で最大の値は、
1,000
です。
そのため、
MAX(NVL(shipping_fee, 0))
の結果は、
1,000
になります。
SUMの場合
購入金額は、
5,000
3,000
2,000
です。
これらを合計すると、
5,000 + 3,000 + 2,000 = 10,000
となります。
そのため、
SUM(NVL(order_price, 0))
の結果は、
10,000
になります。
7. 集計結果はこうなる
ここまでの処理をまとめると、結果は以下のようになります。
| user_id | max_shipping_fee | total_order_price |
|---|---|---|
| 100 | 1,000 | 10,000 |
つまり、
-
GROUP BY→ ユーザーごとにまとめる -
MAX→ そのユーザーの送料の最大値を取得 -
SUM→ そのユーザーの購入金額を合計
という処理をしています。
8. MAXの値が重複したらどうなる?
ここで、初学者が疑問に思いやすいポイントです。
例えば、送料1,000円の注文がもう1件追加されたとします。
集計前のデータ
| user_id | shipping_fee | order_price |
|---|---|---|
| 100 | 500 | 5,000 |
| 100 | NULL | 3,000 |
| 100 | 1,000 | 2,000 |
| 100 | 1,000 | 4,000 |
送料だけを見ると、
500
NULL → 0
1,000
1,000
です。
この中で最大の値は、
1,000
です。
そのため、
MAX(NVL(shipping_fee, 0))
の結果は変わらず、
1,000
となります。
「1,000円が2件あるから2,000円になる」ということはありません。
MAX は値を足し算する関数ではなく、グループ内で最も大きい値を返す関数だからです。
9. 一方、SUMはどうなる?
SUM はMAXとは違います。
4件目の注文には、
order_price = 4,000
が入っています。
そのため、
5,000
+ 3,000
+ 2,000
+ 4,000
となり、
14,000
になります。
結果は以下のようになります。
| user_id | max_shipping_fee | total_order_price |
|---|---|---|
| 100 | 1,000 | 14,000 |
ここが MAX と SUM の大きな違いです。
MAX → 最大の値を返す
SUM → すべての値を合計する
10. NULLを0として扱うNVLとは?
ここまで何度も登場した、
NVL(shipping_fee, 0)
についても確認しておきましょう。
NVL は、
指定した値がNULLだった場合、別の値に置き換える
ための関数です。
基本的な書き方は、
NVL(値, NULLだった場合の値)
です。
例えば、
NVL(shipping_fee, 0)
なら、
shipping_fee = 500
→ 500
shipping_fee = NULL
→ 0
となります。
今回の場合は、
「送料がNULLなら0円として扱いたい」
という意図で使用しています。
11. MAXとNULLの関係
ここは少し注意が必要です。
MAX は、基本的に NULL を無視して最大値を求めます。
例えば、
500
NULL
1,000
であれば、
MAX(shipping_fee)
の結果は、
1,000
です。
そのため、今回のように「NULLが混ざっているだけ」であれば、MAX 自体は最大値を取得できます。
では、なぜ、
MAX(NVL(shipping_fee, 0))
としているのでしょうか?
ポイントは、すべての値がNULLだった場合です。
例えば、
NULL
NULL
NULL
だった場合、
MAX(shipping_fee)
の結果は NULL になります。
一方、
MAX(NVL(shipping_fee, 0))
なら、
0
0
0
として扱うため、結果は、
0
になります。
つまり、
「NULLしかない場合でも0を返したい」
という要件がある場合に、NVL を組み合わせる意味があります。
12. MAXの基本構文を覚えよう
最後に、MAX の基本構文を整理しておきます。
SELECT MAX(カラム名)
FROM テーブル名;
例えば、
SELECT MAX(shipping_fee)
FROM orders;
です。
GROUP BY と組み合わせる場合は、
SELECT
user_id,
MAX(shipping_fee)
FROM orders
GROUP BY user_id;
のようになります。
さらに、NULLを0として扱いたい場合は、
SELECT
user_id,
MAX(NVL(shipping_fee, 0))
FROM orders
GROUP BY user_id;
と書けます。
※ NVL はOracle Databaseなどで使われる関数です。データベース製品によっては、COALESCE や IFNULL などを使用します。
13. この記事のポイントを整理
ここまでの内容を整理すると、以下のようになります。
| SQL | 役割 |
|---|---|
GROUP BY |
指定した項目ごとにデータをグループ化する |
MAX |
グループ内の最大値を返す |
SUM |
グループ内の値を合計する |
NVL |
NULLを指定した値に置き換える |
今回のSQLは、
SELECT
user_id
,MAX(NVL(shipping_fee, 0)) AS max_shipping_fee
,SUM(NVL(order_price, 0)) AS total_order_price
FROM
orders
GROUP BY
user_id;
なので、
ユーザーごとに注文をまとめて、その中から最高送料と購入金額の合計を取得している
と理解すればOKです。
特に覚えておきたいのは、この3つです。
GROUP BY → データをグループ化
MAX → 最大値
SUM → 合計値
そして、
NVL → NULLだった場合の値を指定する
と覚えておけば、今回のSQLの意味がかなり読みやすくなります。
さいごに
GROUP BY や MAX、SUM は、SQLでデータを集計するときに非常によく使われる基本的な機能です。
最初は、
MAX(NVL(...))
のように関数が入れ子になっていると難しく感じるかもしれません。
そんなときは、いきなりSQL全体を理解しようとせず、
① GROUP BYで誰ごとにまとめる?
↓
② MAXで何の最大値を取る?
↓
③ SUMで何を合計する?
↓
④ NULLはどう扱う?
という順番で考えると理解しやすくなります。
SQLの集計処理は、実務でも頻繁に登場します。
まずは、
「GROUP BYでグループを作る → 集計関数でグループ内のデータを集計する」
という基本の考え方をしっかり押さえておきましょう!