SQLite | ゼロからはじめるSQL、30日で習得するSQLite:検索力強化 - Day12 グループ化

SQL SQLite
スポンサーリンク

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 の注文だけに絞って、ユーザーごとの合計購入金額を出したい」

とします。

このときの流れは、

  1. WHERE で「2025-04-01 の注文だけ」に絞る
  2. その結果に対して、user_id ごとに GROUP BY する
  3. 各グループで 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 の組み合わせ、
「集計結果をどう並べて、どう読むか」という実務寄りの話
まで踏み込んで、グループ化の感覚をさらに固めていきます。

タイトルとURLをコピーしました