こんにちは、かつコーチです。
「COUNT・SUM・AVGは使えるようになったけど、『顧客ごとの注文件数』のように、グループ単位で集計したいときはどう書けばいいんだろう」と悩んでいませんか。
この記事では、そんな悩みを解決するGROUP BYの使い方を、実際に手を動かしながら理解できるように解説します。
読み終わる頃には、「◯◯ごとに集計する」というよくあるニーズをSQL一文で解決できるようになります。
GROUP BYとは?
グループごとに集計するための句
GROUP BYとは、指定した列の値が同じ行を一つのグループにまとめ、そのグループ単位で集計関数(COUNT・SUM・AVG・MAX・MINなど、複数行の値をまとめて一つの値にする関数)を適用するための句です。
以前の記事「集計関数(COUNT・SUM・AVG・MAX・MIN)の使い方」では、テーブル全体を一つの塊として集計する方法を紹介しました。
GROUP BYを使うと、テーブル全体ではなく「顧客ごと」「日付ごと」「商品ごと」といった単位で集計できるようになります。
なぜGROUP BYが必要なのか
たとえば「顧客ごとの注文件数を知りたい」という要望は、実務でとてもよく出てきます。
GROUP BYを知らないと、顧客ごとにSELECT文を何回も実行して手動で集計する、といった非効率な方法に頼ってしまいがちです。
GROUP BYを使えば、この作業をSQL一文で完結させられます。
基本の書き方
この記事では、以下のサンプルテーブルを使います。
CREATE TABLE customers (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
prefecture VARCHAR(20) NOT NULL
);
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
customer_id INT NOT NULL,
order_date DATE NOT NULL,
total_amount INT NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
INSERT INTO customers (name, prefecture) VALUES
('田中', '東京都'),
('佐藤', '大阪府'),
('鈴木', '東京都');
INSERT INTO orders (customer_id, order_date, total_amount) VALUES
(1, '2026-08-01', 3000),
(1, '2026-08-10', 5000),
(2, '2026-08-03', 2000),
(3, '2026-08-05', 7000);
手順1:単一の列でグループ化する
顧客ごとの注文件数を数えるには、次のように書きます。
SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id;
実行結果は以下のようになります。
| customer_id | order_count |
|---|---|
| 1 | 2 |
| 2 | 1 |
| 3 | 1 |
customer_idが同じ行を1グループにまとめ、そのグループごとにCOUNT(*)を計算しています。
手順2:SUM・AVGと組み合わせる
合計金額や平均金額を出したいときも同じ考え方です。
SELECT customer_id, SUM(total_amount) AS total_spent, AVG(total_amount) AS avg_amount
FROM orders
GROUP BY customer_id;
顧客ごとの合計購入額・平均購入額が一気に取得できます。
手順3:複数の列でグループ化する
GROUP BYには複数の列を指定することもできます。
SELECT customer_id, order_date, SUM(total_amount) AS daily_total
FROM orders
GROUP BY customer_id, order_date;
この場合は「顧客IDと日付の組み合わせ」が同じ行を1グループとして扱います。
「同じ顧客が同じ日に複数回注文した場合の合計」を出したいときに便利です。
つまずきやすい設定・注意点
SELECT句に書ける列の制限
GROUP BYを使うときに最初につまずきやすいのが、SELECT句に書ける列の制限です。
❌ Before:GROUP BYで指定していない列を、集計関数を通さずにそのままSELECT句に書いてしまう
SELECT customer_id, name, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id;
このクエリはMySQLの設定によってはエラーにならず動いてしまうことがありますが、name列がどの行の値を表しているのか論理的に不定になり、意図しない結果を招きます。
✅ After:GROUP BYで指定した列か、集計関数を通した列だけをSELECT句に書く
SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id;
私が実際にこのミスをしたときは、開発環境の設定(sql_modeにONLY_FULL_GROUP_BYが含まれていない状態)ではエラーにならず、本番環境に近い設定のサーバーに移した途端にERROR 1055が出て焦った経験があります。
「エラーが出ないから正しい」とは限らないので、GROUP BYを使うときは「SELECT句にはGROUP BYの列か集計関数だけ」というルールを徹底しておくと安全です。
GROUP BYとWHEREの実行順序
もう一つ注意したいのが、WHERE句とGROUP BYの実行順序です。
SQLは書いた順番ではなく、FROM → WHERE → GROUP BY → SELECTという決まった順序で実行されます。
つまり、WHERE句はグループ化される「前」の行を絞り込む条件であり、グループ化した「後」の結果を絞り込むことはできません。
SELECT customer_id, COUNT(*) AS order_count
FROM orders
WHERE order_date >= '2026-08-01'
GROUP BY customer_id;
このクエリは「2026年8月1日以降の注文だけを対象に」グループ化しています。
グループ化した後の結果(たとえば「注文件数が2件以上の顧客だけ」)を絞り込みたい場合は、次回解説するHAVING句を使う必要があります。
応用・一歩先の使い方
ORDER BYと組み合わせて集計結果を並び替える
GROUP BYの結果は、ORDER BYと組み合わせることでランキング的な使い方もできます。
SELECT customer_id, SUM(total_amount) AS total_spent
FROM orders
GROUP BY customer_id
ORDER BY total_spent DESC;
「購入金額が多い顧客順」に並び替えられるので、優良顧客の分析などにそのまま使えます。
GROUP BYと集計結果の絞り込みへ
GROUP BYで集計した結果を「条件で絞り込みたい」というニーズは非常に多く出てきます。
その際に使うのがHAVING句です。
次回の記事「HAVINGでグループ集計結果を絞り込む」で、WHEREとの違いを含めて詳しく解説します。
まとめ
この記事のポイント
- GROUP BYは指定した列の値が同じ行をグループ化し、集計関数を適用するための句
- SELECT句にはGROUP BYで指定した列か、集計関数を通した列だけを書く
- SQLは
FROM → WHERE → GROUP BY → SELECTの順に実行されるため、WHEREはグループ化前の絞り込みにしか使えない - 複数列を指定すれば、組み合わせ単位でのグループ化も可能
次に読むべき記事
グループ化した結果をさらに条件で絞り込みたい方は、次回の「HAVINGでグループ集計結果を絞り込む」に進んでください。
タグ: SQL, 初心者向け, 集計
