SQLのGROUP BYとは?部署ごとの件数・合計・平均を結果表で解説
GROUP BYは、同じ値を持つ行をまとめて集計する構文です。社員5人の一覧から「部署ごとの人数や月給の合計」を作れます。どの社員が同じグループになり、結果が何行になるかを確かめましょう。
例で使う表
employeesの1行が社員1人分です。営業が2人、開発が2人、サポートが1人います。salaryは月給(円)で、恵比寿さんのNULLは月給が未入力であることを表し、0円とは区別します。すべての例でこの5人を使います。結果表の行の並びは一例です。ORDER BYで指定しない限り、表示順は保証されません。
| id(社員ID) | name(名前) | department(部署) | salary(月給) |
|---|---|---|---|
| 1 | 青木 | 営業 | 300000 |
| 2 | 北野 | 開発 | 400000 |
| 3 | 中央 | 営業 | 350000 |
| 4 | 大和 | 開発 | 400000 |
| 5 | 恵比寿 | サポート | NULL |
GROUP BYの基本構文
SELECT department, COUNT(*) AS people
FROM employees
GROUP BY department;部署ごとにまとめてCOUNTで人数を数える
GROUP BY departmentで部署が同じ社員をまとめ、COUNT(*)でその人数を数えます。営業は青木さんと中央さんの2人、開発も2人、サポートは1人です。元の5行が部署ごとの3行になります。AS peopleは人数の結果列に付けた名前です。
SELECT department, COUNT(*) AS people
FROM employees
GROUP BY department;| department | people |
|---|---|
| サポート | 1 |
| 営業 | 2 |
| 開発 | 2 |
SUMで部署ごとの月給を合計する
COUNT(*)をSUM(salary)に変えると、人数の代わりに月給を合計します。営業は300000 + 350000で650000円、開発は400000 + 400000で800000円です。サポートは月給がNULLの1人だけなので、合計もNULLになります。
SELECT department, SUM(salary) AS total_salary
FROM employees
GROUP BY department;| department | total_salary |
|---|---|
| サポート | NULL |
| 営業 | 650000 |
| 開発 | 800000 |
AVGで部署ごとの平均月給を求める
AVG(salary)は、入力されている月給の合計をその件数で割ります。営業の平均は650000 ÷ 2で325000円、開発は400000円です。NULLを0円として計算するわけではなく、入力値のないサポートは平均もNULLです。
SELECT department, AVG(salary) AS average_salary
FROM employees
GROUP BY department;| department | average_salary |
|---|---|
| サポート | NULL |
| 営業 | 325000 |
| 開発 | 400000 |
MIN・MAXで部署の月給の幅を見る
MINは最小値、MAXは最大値です。営業では300000円と350000円、開発では全員が同額なので両方とも400000円になります。表示されるのは金額であり、その金額を受け取る社員名ではありません。
SELECT department,
MIN(salary) AS lowest_salary,
MAX(salary) AS highest_salary
FROM employees
GROUP BY department;| department | lowest_salary | highest_salary |
|---|---|---|
| サポート | NULL | NULL |
| 営業 | 300000 | 350000 |
| 開発 | 400000 | 400000 |
COUNT(*)とCOUNT(salary)の違いを確かめる
COUNT(*)は社員の行を数え、COUNT(salary)は月給がNULLでない行だけを数えます。サポートには社員が1人いるのでpeopleは1ですが、入力済み月給の件数は0です。「人数」と「値が入っている件数」を区別しましょう。
SELECT department,
COUNT(*) AS people,
COUNT(salary) AS entered_salaries
FROM employees
GROUP BY department;| department | people | entered_salaries |
|---|---|---|
| サポート | 1 | 0 |
| 営業 | 2 | 2 |
| 開発 | 2 | 2 |
COUNT DISTINCTで月給が何種類あるかを数える
COUNT(DISTINCT salary)は、重複を除いた月給の種類数です。営業は300000円と350000円の2種類、開発は2人とも400000円なので1種類になります。NULLは数えないためサポートは0です。
SELECT department, COUNT(DISTINCT salary) AS salary_types
FROM employees
GROUP BY department;| department | salary_types |
|---|---|
| サポート | 0 |
| 営業 | 2 |
| 開発 | 1 |
複数列で「部署と月給」の組み合わせ別にまとめる
GROUP BY department, salaryとすると、部署だけでなく月給も同じ社員をまとめます。営業は月給が違うので2グループに分かれ、開発の2人は同じグループです。部署だけの3行から、結果が4行へ増える点に注目してください。
SELECT department, salary, COUNT(*) AS people
FROM employees
GROUP BY department, salary;| department | salary | people |
|---|---|---|
| サポート | NULL | 1 |
| 営業 | 300000 | 1 |
| 営業 | 350000 | 1 |
| 開発 | 400000 | 2 |
WHEREで集計する社員を先に絞る
月給35万円以上の社員だけを数えるなら、GROUP BYの前にWHEREを書きます。営業の青木さんと、月給が未入力の恵比寿さんが集計対象から外れます。その結果、営業は1人、開発は2人になり、サポートの行はなくなります。
SELECT department, COUNT(*) AS people
FROM employees
WHERE salary >= 350000
GROUP BY department;| department | people |
|---|---|
| 営業 | 1 |
| 開発 | 2 |
実際に1問解いてみる
GROUP BY・COUNT(*)
employeesの部署ごとの人数を、department、COUNT(*)の順で取得してください。行順は問いません。
テーブル
読み込み中…
実行結果
関連構文
GROUP BYについてよくある質問
- 社員名もSELECTに書いてよいですか?
- 部署だけでまとめると、営業の名前を青木と中央のどちらにするか決まりません。この例でnameをそのままSELECTに加えるとエラーになります。社員ごとの行を残したい場合は、ウィンドウ関数のガイドを参照してください。
- 0人の部署も表示できますか?
- この社員表に存在しない部署は、GROUP BYだけでは表示されません。部署の一覧を別に用意してLEFT JOINする方法があります。
- DISTINCTとの違いは?
- 部署名の重複を除くだけならDISTINCTで十分です。部署ごとの人数や金額も計算したいときにGROUP BYを使います。
参考文献
内容はPostgreSQL公式ドキュメントを参照し、初めて学ぶ人向けに日本語で整理しています。
- PostgreSQL公式:GROUP BYとHAVING — GROUP BYとHAVINGの仕様と使用例
- PostgreSQL公式:集約関数 — 集約関数の仕様と使用例
- PostgreSQL公式:集約式 — 集約式の仕様と使用例