Day12 前半
「1件ずつ」ではなく「グループごと」に数字を見る世界に入る
ここまでで、
COUNT / SUM / AVG / MAX / MIN を使って
「テーブル全体」や「WHERE で絞った一部」を数字で見るところまで来ました。
Day12 からは、もう一段ギアを上げます。
テーマは GROUP BY(グループ化)。
一言でいうと、
「ユーザーごとに合計金額を出したい」
「日付ごとにアクセス件数を数えたい」
といった、“グループ単位の集計”を一気に出すための仕組み です。
GROUP BY の直感的なイメージ
「同じ値ごとに行を束ねて、その束ごとに集計する」
まずは、イメージから固めます。
たとえば、次のような orders テーブルがあるとします。
id | user_id | amount
---+---------+-------
1 | 1 | 1200
2 | 1 | 3000
3 | 2 | 500
4 | 3 | 8000
5 | 2 | 1500
ここで、
「ユーザーごとに、購入金額の合計を出したい」
という問いを考えます。
人間の頭の中では、こんなことをしています。
user_id = 1 の行を集める → 1200 と 3000 → 合計 4200
user_id = 2 の行を集める → 500 と 1500 → 合計 2000
user_id = 3 の行を集める → 8000 → 合計 8000
この「user_id ごとに行を束ねて、その束ごとに SUM する」という操作を、
SQL で一発でやるのが GROUP BY です。
GROUP BY の基本形
「どの列でグループを作るか」を宣言する
GROUP BY の基本形はこうです。
SELECT グループのキーになる列,
集計関数(列)
FROM テーブル名
GROUP BY グループのキーになる列;
SQL先ほどの「ユーザーごとの合計購入金額」を SQL にすると、こうなります。
SELECT user_id,
SUM(amount) AS total_amount
FROM orders
GROUP BY user_id;
SQLここで起きていることを、丁寧に言葉にするとこうです。
まず、user_id の値ごとに行をグループに分ける。
user_id = 1 のグループ、user_id = 2 のグループ、user_id = 3 のグループ。
次に、それぞれのグループの中で SUM(amount) を計算する。
結果はこうなります。
user_id | total_amount
--------+-------------
1 | 4200
2 | 2000
3 | 8000
ここで重要なのは、
SELECT に書ける「普通の列」は、GROUP BY にも書かれている列だけ
というルールです(集計関数の中に入っている列は別)。
つまり、
SELECT user_id, SUM(amount)
GROUP BY user_id
はOKだけど、
SELECT user_id, amount
GROUP BY user_id
のように書くと、「amount をどう扱うの?」となってエラーになります。
(どの行の amount を出せばいいか決まらないからです。)
「1行 = 1レコード」から「1行 = 1グループ」へ発想を切り替える
GROUP BY を理解するうえで、一番大事な発想の転換はここです。
普通の SELECT
「1行 = テーブルの1レコード」
GROUP BY を使った SELECT
「1行 = 1つのグループ(user_id ごと、日付ごと、など)」
さっきの例でいうと、
元のテーブルは 5 行あったのに、
GROUP BY user_id をすると 3 行になります。
これは、「3人のユーザー(3グループ)にまとめたから」です。
この「1行の意味が変わる」という感覚をつかめると、
GROUP BY への苦手意識がかなり減ります。
GROUP BY と COUNT を組み合わせる
「ユーザーごとの注文回数」を一気に出す
SUM だけでなく、COUNT と組み合わせるのも超定番です。
同じ orders テーブルで、
「ユーザーごとの注文回数を知りたい」
とします。
人間の頭の中では、
user_id = 1 → 行が2つ → 2回
user_id = 2 → 行が2つ → 2回
user_id = 3 → 行が1つ → 1回
ということをやっています。
SQL ではこう書きます。
SELECT user_id,
COUNT(*) AS order_count
FROM orders
GROUP BY user_id;
SQL結果はこうなります。
user_id | order_count
--------+------------
1 | 2
2 | 2
3 | 1
ここでも流れは同じです。
user_id ごとに行をグループに分ける。
そのグループの中の行数を COUNT(*) で数える。
「GROUP BY でグループを作り、その中で集計関数を使う」
これが、Day12 の核になるパターンです。
GROUP BY と WHERE の関係
「どの行をグループ化の対象にするか」を先に決める
GROUP BY と WHERE を組み合わせるときの流れも、しっかり押さえておきましょう。
たとえば、
「2025-04-01 の注文だけに絞って、ユーザーごとの合計購入金額を出したい」
とします。
このときの流れは、
- WHERE で「2025-04-01 の注文だけ」に絞る
- その結果に対して、user_id ごとに GROUP BY する
- 各グループで SUM(amount) を計算する
SQL はこうなります。
SELECT user_id,
SUM(amount) AS total_amount
FROM orders
WHERE date(created_at) = '2025-04-01'
GROUP BY user_id;
SQL大事なのは、
「WHERE は GROUP BY より先に効く」
ということです。
「どの行をグループ化の対象にするか」を WHERE で決めてから、
その中でグループを作って集計する、という順番をイメージしてください。
セキュリティの視点から見る GROUP BY の意味
「誰ごとに」「いつごとに」異常を見たいか、という設計
GROUP BY は、セキュリティの観点でもかなり重要です。
たとえば、
ユーザーごとのログイン試行回数(GROUP BY user_id, COUNT())
IPアドレスごとのアクセス件数(GROUP BY ip_address, COUNT())
日付ごとのエラー件数(GROUP BY date(created_at), COUNT(*))
といった集計は、
「どのユーザーが怪しいか」
「どのIPが攻撃っぽいか」
「どの日に異常が集中しているか」
を見つけるための基本的なレーダーになります。
Day12 前半の段階では、
「GROUP BY は、“誰ごとに”“いつごとに”といった単位で、数字をまとめて見るためのものだ」
という感覚だけ持っておいてくれれば十分です。
Day12 前半のまとめ
GROUP BY は、「同じ値ごとに行をグループに分け、そのグループごとに集計する」ための句。
SELECT に書ける普通の列は、基本的に GROUP BY にも書かれている列だけ(集計関数の中は別)。
「1行 = 1レコード」ではなく、「1行 = 1グループ(user_id ごと、日付ごとなど)」という発想に切り替えるのが重要。
GROUP BY と COUNT / SUM を組み合わせると、「ユーザーごとの注文回数」「ユーザーごとの合計購入金額」などが一発で出せる。
WHERE は GROUP BY より先に効き、「どの行をグループ化の対象にするか」を決める役割を持つ。
後半では、
複数列での GROUP BY(user_id × 日付 など)、
GROUP BY と ORDER BY の組み合わせ、
「集計結果をどう並べて、どう読むか」という実務寄りの話
まで踏み込んで、グループ化の感覚をさらに固めていきます。
