SQLのGROUP BYとは?複数列・COUNT・NULLの集計と使い方
GROUP BYは、同じ値の行をまとめて、部署別の人数や顧客別の売上を求める構文です。最初に「結果の1行は何を表すか」を決めると、集計のズレを防げます。基本の件数集計から、NULL・重複・条件付き集計の使い分けまで順に確認します。
GROUP BYの基本構文
SELECT department, COUNT(*)
FROM employees
GROUP BY department;GROUP BYの基本:部署ごとにCOUNTで人数を数える
GROUP BY departmentで部署の値が同じ行を1グループにし、COUNT(*)で各グループの行数を数えます。Salesが2行、Supportが1行なら結果は2行で、人数はそれぞれ2と1です。集計単位以外の明細列をそのままSELECTすると、どの行の値を出すか決まらないため原則エラーになります。
SELECT department, COUNT(*)
FROM employees
GROUP BY department;SUMで合計を求める
SUM(salary)は部署内の給与を足し合わせます。人数ではなく人件費を知りたいときに使います。SUMの対象列は集約関数の中にあるためGROUP BYへ追加しません。salaryもGROUP BYに書くと、部署と給与の組み合わせ別になってしまいます。
SELECT department, SUM(salary)
FROM employees
GROUP BY department;AVGで平均を求める:NULLを0とみなさない
AVGはNULLでない値だけで平均を計算します。給与が100、200、NULLなら平均は150で、100にはなりません。未入力を0として扱う業務要件ならAVG(COALESCE(salary, 0))ですが、不明な値を0とみなしてよいかを先に確認しましょう。
SELECT department, AVG(salary)
FROM employees
GROUP BY department;MIN・MAXで最小値と最大値を調べる
MINとMAXは値の下限と上限を返します。給与の幅や最初・最後の注文日を調べるときに使えます。ただしMAX(salary)だけでは最高給与の社員名は得られません。その明細も必要なら、サブクエリやウィンドウ関数で対象行を選びます。
SELECT department, MIN(salary), MAX(salary)
FROM employees
GROUP BY department;複数列のGROUP BY:値の組み合わせで集計する
GROUP BY department, activeは「部署×在籍状態」ごとに1行を作ります。列を増やすほど結果の粒度が細かくなるため、部署別人数より行数が増えることがあります。GROUP BYに並び順の保証はなく、表示順が必要ならORDER BYを追加します。
SELECT department, active, COUNT(*)
FROM employees
GROUP BY department, active
ORDER BY department, active;COUNT(*)とCOUNT(列)の違い:NULLの件数
COUNT(*)は全行、COUNT(salary)は給与がNULLでない行を数えます。差を取れば未入力件数になります。一方、集計キーのdepartmentがNULLの行は、GROUP BYでは1つのグループにまとまります。NULLを集計から除きたい場合は、WHEREで明示的に除外してください。
SELECT department, COUNT(*), COUNT(salary)
FROM employees
GROUP BY department;COUNT DISTINCTで重複しない値を数える
COUNT(DISTINCT salary)は、部署内で何種類の給与があるかを数えます。人数ではなく種類数を求める集計です。同額の人が複数いても1種類で、NULLは数えません。顧客IDへ置き換えれば、購入回数ではなく購入者数を求められます。
SELECT department, COUNT(DISTINCT salary)
FROM employees
GROUP BY department;FILTERで条件付き集計を同じ行に並べる
FILTERは、その集約関数に渡す行だけを絞ります。在籍者数と全社員数を横に並べたいとき、WHEREで在籍者だけにすると全社員数まで減ってしまいます。PostgreSQLでは次のように書けます。他のDBへ移植する際はFILTERの対応状況を確認してください。
SELECT
department,
COUNT(*),
COUNT(*) FILTER (WHERE active) AS active_count
FROM employees
GROUP BY department;WHEREとGROUP BY:集計対象を先に絞る
在籍者だけの部署別人数なら、WHERE active = trueで対象を決めてから集計します。考え方はFROM → WHERE → GROUP BY → HAVING → ORDER BYです。これは結果を理解するための論理的な順序で、DB内部の物理的な実行順序とは区別します。集計した人数自体で絞る方法はHAVINGガイドで扱います。
SELECT department, COUNT(*)
FROM employees
WHERE active = true
GROUP BY department
ORDER BY department;GROUP BYでよくある間違い
0件の集計と「存在しないグループ」は別
GROUP BYなしのCOUNTは入力0件でも0を返しますが、GROUP BYありではグループ自体がなく結果は0行です。SUMは入力0件でNULLになります。存在しない部署を0人で表示するには部署マスタを起点としたLEFT JOINなどが必要で、COALESCEだけでは行を作れません。
SELECT COUNT(*), SUM(salary)
FROM employees
WHERE false;
SELECT department, COUNT(*)
FROM employees
WHERE false
GROUP BY department;実際に1問解いてみる
GROUP BY・COUNT(*)
employeesの部署ごとの人数を、department、COUNT(*)の順で取得してください。行順は問いません。
テーブル
読み込み中…
実行結果
関連構文
GROUP BYについてよくある質問
- GROUP BYとDISTINCTはどう使い分けますか?
- 重複のない一覧を作るだけならDISTINCT、グループ別に件数や合計を計算するならGROUP BYを使います。どちらも結果の並び順を保証しません。
- GROUP BYにない列をSELECTするとエラーになるのはなぜですか?
- 1グループに複数の値があり、返す値が決まらないためです。集計キーへ追加するか、MINなどで選び方を指定します。PostgreSQLでは同じ基本テーブルの主キーをグループ化すると、主キーに従属する列を省略できる場合があります。
- GROUP BYとPARTITION BYの違いは?
- GROUP BYはグループを1行にまとめます。ウィンドウ関数のPARTITION BYは明細行を残して計算範囲を分けます。社員一覧に部署平均を添えるなら後者です。
参考文献
内容はPostgreSQL公式ドキュメントを参照し、初めて学ぶ人向けに日本語で整理しています。
- PostgreSQL公式:GROUP BYとHAVING — 集計単位、集約前後の条件、選択列の制約
- PostgreSQL公式:集約関数 — NULL、空集合、COUNT・SUM・AVG・GROUPING
- PostgreSQL公式:集約式 — DISTINCTとFILTER
- PostgreSQL公式:ROLLUPとGROUPING SETS — 小計と総計の生成