この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:4
ページ更新者:guest
更新日時:2026-06-11 07:07:02

タイトル: GROUP BY 句
SEOタイトル: SQL GROUP BY 句 完全ガイド(集計関数 / HAVING / ROLLUP / NULL 扱い / 複数列)

この記事の要点
  • GROUP BY 句は同じ値の行を1 グループにまとめる SQL 構文。集計関数 (COUNT / SUM / AVG など) と組み合わせて使う
  • 構文: SELECT 列, 集計関数(列) FROM 表 GROUP BY 列;
  • SELECT に書ける列はGROUP BY に書いた列集計関数の結果のみ
  • 集計結果の絞り込みは HAVING 句(WHERE は集計前、HAVING は集計後)
  • 応用: ROLLUP / CUBE / GROUPING SETS で小計・総計を自動付与
  • NULL は同じグループとして扱われる(DB ベンダごとに違いに注意)

GROUP BY 句とは

GROUP BY 句は SQL で行をグループにまとめるための句です。指定した列の値が同じ行を 1 グループとし、集計関数と組み合わせてグループごとの集計値を取得します。

「部署ごとの平均給与」「日付ごとの売上合計」「商品カテゴリ別の在庫数」など、分析・レポートの基本道具です。

基本構文

SELECT
    グループ化する列,
    集計関数(対象列)
FROM テーブル
WHERE 条件             -- 集計前フィルタ
GROUP BY グループ化する列
HAVING 条件            -- 集計後フィルタ
ORDER BY 並び順;

サンプルデータ

idnamedeptsalary
1Alice営業400
2Bob開発500
3Carol営業450
4Dave開発600
5Eve人事380

1. 単一列でグループ化

SELECT dept, COUNT(*) AS cnt, AVG(salary) AS avg_sal
FROM employees
GROUP BY dept;
deptcntavg_sal
営業2425
開発2550
人事1380

2. 複数列でグループ化

SELECT dept, gender, COUNT(*) AS cnt
FROM employees
GROUP BY dept, gender;

dept と gender の組み合わせで 1 グループ。「営業×女性」「営業×男性」「開発×女性」… のようにまとめられます。

3. 主な集計関数

関数意味
COUNT(*)行数
COUNT(列)NULL 以外の行数
COUNT(DISTINCT 列)重複除外した値の数
SUM(列)合計
AVG(列)平均
MAX(列)最大
MIN(列)最小
STDDEV(列) / VARIANCE(列)標準偏差 / 分散
GROUP_CONCAT(列) (MySQL)グループ内の値をカンマ連結
STRING_AGG(列, ',') (PostgreSQL / SQL Server)同上の標準形

4. WHERE と HAVING の違い

適用タイミング集計関数使用
WHEREグループ化に行をフィルタ不可
HAVINGグループ化に結果をフィルタ
-- 営業部だけ集計し、平均給与が 400 以上のグループのみ表示
SELECT dept, AVG(salary) AS avg_sal
FROM employees
WHERE join_year >= 2020         -- 集計前: 2020 年以降入社のみ
GROUP BY dept
HAVING AVG(salary) >= 400       -- 集計後: 平均 400 以上
ORDER BY avg_sal DESC;

5. SELECT に書ける列のルール

標準 SQL では、SELECT 句に書ける列は次のいずれかでなければエラー。

  • GROUP BY 句に書いた列
  • 集計関数の結果
  • 定数
-- NG: name は GROUP BY にも集計関数にもない
SELECT dept, name, COUNT(*) FROM employees GROUP BY dept;

-- OK
SELECT dept, COUNT(*) FROM employees GROUP BY dept;

-- OK: name も GROUP BY に入れた
SELECT dept, name, COUNT(*) FROM employees GROUP BY dept, name;

MySQL は古いバージョンで緩く動いてしまうが、ONLY_FULL_GROUP_BY モード(5.7+ 既定)で標準的にエラーになる。

6. ROLLUP / CUBE / GROUPING SETS

標準 SQL の集計拡張。小計・総計を自動的に付与してくれる。

-- ROLLUP: 階層的に集計 (部署別 + 全社合計)
SELECT dept, SUM(salary)
FROM employees
GROUP BY ROLLUP(dept);
-- 結果:
-- 営業   850
-- 開発   1100
-- 人事   380
-- (全体) 2330       ← ROLLUP の追加行 (dept は NULL)

-- CUBE: 全ての組み合わせを集計
SELECT dept, gender, SUM(salary)
FROM employees
GROUP BY CUBE(dept, gender);
-- dept×gender, dept のみ, gender のみ, 全体 の全集計

-- GROUPING SETS: 指定した組み合わせだけ
SELECT dept, gender, SUM(salary)
FROM employees
GROUP BY GROUPING SETS ((dept), (gender), ());

7. NULL の扱い

GROUP BY では NULL も 1 グループとして扱われます(NULL = NULL とみなされる特例)。

SELECT category, COUNT(*)
FROM products
GROUP BY category;
-- category が NULL の商品も 1 グループとして集計される

8. 実行順序(SQL 句の評価順)

役割
1FROM / JOIN対象テーブルの結合
2WHERE行のフィルタ
3GROUP BYグループ化
4HAVINGグループのフィルタ
5SELECT列の選択・計算
6ORDER BY並び替え
7LIMIT / OFFSET件数制限

この順序を覚えると、SELECT のエイリアスが WHERE で使えないなどの仕様が腑に落ちます。

FAQ

Q: GROUP BY なしで集計関数だけ書いたら?
A: テーブル全体が 1 グループとして扱われ、1 行返る。SELECT COUNT(*) FROM employees; で全社員数。

Q: DISTINCT との違い
A: DISTINCT は重複を除くだけで集計不可。GROUP BY は集計可能。値の一覧だけ欲しいなら DISTINCT のほうが意図が明確。

Q: 集計関数を WHERE に書きたい
A: 不可。HAVING を使う。またはサブクエリで集計後の結果に WHERE。

Q: パフォーマンス
A: GROUP BY 対象列にインデックスがあると高速。複数列の場合は複合インデックスが効果的。EXPLAIN で確認。