Skip to main content

データの集計

この章で得られるスキル:

  • ✅ 集約関数(COUNT、SUM、AVG、MAX、MIN)を使える
  • ✅ GROUP BYでデータをグループ化して集計できる
  • ✅ HAVING句で集計結果を絞り込める
  • ✅ WHEREとHAVINGの違いを説明できる
  • ✅ NULLが集計に与える影響を理解している

Step 0: まず体験してみよう

シナリオ:部門ごとの平均給与を知りたい

「各部門の平均給与はいくらか?」を調べたいとする。 Excelならフィルタやピボットテーブルを使う必要があるが、SQLなら1行で済む。

CREATE TABLE departments ( dept_id INTEGER PRIMARY KEY, dept_name VARCHAR(50) NOT NULL UNIQUE, location VARCHAR(50) ); CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER REFERENCES departments(dept_id), hire_date DATE NOT NULL, salary INTEGER CHECK (salary > 0), email VARCHAR(100) UNIQUE ); INSERT INTO departments VALUES (1, '営業部', '東京'); INSERT INTO departments VALUES (2, '開発部', '大阪'); INSERT INTO departments VALUES (3, '人事部', '東京'); INSERT INTO employees VALUES (1, '田中太郎', 1, '2020-04-01', 350000, 'tanaka@example.com'); INSERT INTO employees VALUES (2, '佐藤花子', 2, '2021-04-01', 400000, 'sato@example.com'); INSERT INTO employees VALUES (3, '鈴木一郎', 2, '2022-04-01', 320000, 'suzuki@example.com'); INSERT INTO employees VALUES (4, '高橋美咲', 3, '2023-04-01', 280000, 'takahashi@example.com'); INSERT INTO employees VALUES (5, '伊藤健太', 1, '2021-10-01', 330000, 'ito@example.com'); INSERT INTO employees VALUES (6, '渡辺あかり', 2, '2023-07-01', 300000, 'watanabe@example.com'); INSERT INTO employees VALUES (7, '山本裕子', 1, '2022-04-01', 310000, 'yamamoto@example.com'); INSERT INTO employees VALUES (8, '中村大輔', 3, '2020-10-01', 360000, 'nakamura@example.com'); -- 部門ごとの平均給与(SQLなら1行!) SELECT dept_id, AVG(salary) AS 平均給与 FROM employees GROUP BY dept_id;

SQLの集計機能を使えば、 大量のデータから統計情報を瞬時に 取得できる。


Step 1: 集約関数の基本

5つの集約関数

集約関数は、 複数の行を1つの値にまとめる 関数である。

関数意味
COUNT(*)行数を数える社員は全部で何人か
SUM(列名)合計を求める給与の合計
AVG(列名)平均を求める給与の平均
MAX(列名)最大値を求める最高給与
MIN(列名)最小値を求める最低給与
CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, salary INTEGER ); INSERT INTO employees VALUES (1, '田中太郎', 350000); INSERT INTO employees VALUES (2, '佐藤花子', 400000); INSERT INTO employees VALUES (3, '鈴木一郎', 320000); INSERT INTO employees VALUES (4, '高橋美咲', 280000); INSERT INTO employees VALUES (5, '伊藤健太', 330000); -- 社員数 SELECT COUNT(*) AS 社員数 FROM employees; -- 給与の合計 SELECT SUM(salary) AS 給与合計 FROM employees; -- 給与の平均 SELECT AVG(salary) AS 平均給与 FROM employees; -- 最高給与と最低給与 SELECT MAX(salary) AS 最高給与, MIN(salary) AS 最低給与 FROM employees; -- まとめて表示 SELECT COUNT(*) AS 社員数, SUM(salary) AS 給与合計, AVG(salary) AS 平均給与, MAX(salary) AS 最高給与, MIN(salary) AS 最低給与 FROM employees;

Step 2: COUNTのバリエーション

COUNT(*)、COUNT(列名)、COUNT(DISTINCT 列名)の違い

CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER, email VARCHAR(100) ); INSERT INTO employees VALUES (1, '田中太郎', 1, 'tanaka@example.com'); INSERT INTO employees VALUES (2, '佐藤花子', 2, 'sato@example.com'); INSERT INTO employees VALUES (3, '鈴木一郎', 2, NULL); INSERT INTO employees VALUES (4, '高橋美咲', 1, NULL); INSERT INTO employees VALUES (5, '伊藤健太', 1, 'ito@example.com'); -- COUNT(*): NULLを含む全行数 SELECT COUNT(*) AS 全行数 FROM employees; -- COUNT(列名): NULLを除いた行数 SELECT COUNT(email) AS メール登録済み FROM employees; -- COUNT(DISTINCT 列名): 重複を除いた件数 SELECT COUNT(DISTINCT dept_id) AS 部門数 FROM employees;
書き方意味上の例の結果
COUNT(*)全行数(NULLを含む)5
COUNT(email)NULLを除いた行数3
COUNT(DISTINCT dept_id)重複を除いた件数2
重要

COUNT(*)COUNT(列名) は結果が異なる場合がある。 COUNT(列名)NULLの行を数えない ため、NULLがあるテーブルでは注意が必要である。


Step 3: NULLと集約関数

集約関数はNULLを無視する

SUM、AVG、MAX、MINは、NULLの値を 無視して 計算する。

CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, bonus INTEGER ); INSERT INTO employees VALUES (1, '田中太郎', 50000); INSERT INTO employees VALUES (2, '佐藤花子', NULL); INSERT INTO employees VALUES (3, '鈴木一郎', 30000); INSERT INTO employees VALUES (4, '高橋美咲', NULL); INSERT INTO employees VALUES (5, '伊藤健太', 20000); -- AVGはNULLを無視して計算する SELECT AVG(bonus) AS 平均ボーナス FROM employees; -- → (50000 + 30000 + 20000) / 3 = 33333(NULLの2人は除外) -- NULLを0として扱いたい場合はCOALESCE SELECT AVG(COALESCE(bonus, 0)) AS 平均ボーナス FROM employees; -- → (50000 + 0 + 30000 + 0 + 20000) / 5 = 20000
注意

AVG はNULLを 0ではなく「存在しない」として扱う 。 そのため、NULLを含む列の平均を計算すると、NULLの行は分母に含まれない。 NULLを0として扱いたい場合は COALESCE(列名, 0) を使う。


Step 4: GROUP BY(グループ化)

GROUP BYの基本

GROUP BY を使うと、指定した列の値ごとにデータを グループ化 し、各グループに対して集約関数を適用できる。

SELECT グループ化する列, 集約関数() FROM テーブル名 GROUP BY グループ化する列;
CREATE TABLE departments ( dept_id INTEGER PRIMARY KEY, dept_name VARCHAR(50) NOT NULL UNIQUE ); CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER REFERENCES departments(dept_id), salary INTEGER ); INSERT INTO departments VALUES (1, '営業部'); INSERT INTO departments VALUES (2, '開発部'); INSERT INTO departments VALUES (3, '人事部'); INSERT INTO employees VALUES (1, '田中太郎', 1, 350000); INSERT INTO employees VALUES (2, '佐藤花子', 2, 400000); INSERT INTO employees VALUES (3, '鈴木一郎', 2, 320000); INSERT INTO employees VALUES (4, '高橋美咲', 3, 280000); INSERT INTO employees VALUES (5, '伊藤健太', 1, 330000); INSERT INTO employees VALUES (6, '渡辺あかり', 2, 300000); INSERT INTO employees VALUES (7, '山本裕子', 1, 310000); INSERT INTO employees VALUES (8, '中村大輔', 3, 360000); -- 部門ごとの社員数 SELECT dept_id, COUNT(*) AS 社員数 FROM employees GROUP BY dept_id; -- 部門ごとの平均給与 SELECT dept_id, AVG(salary) AS 平均給与 FROM employees GROUP BY dept_id; -- 部門ごとの最高給与と最低給与 SELECT dept_id, MAX(salary) AS 最高給与, MIN(salary) AS 最低給与 FROM employees GROUP BY dept_id;

GROUP BYの動作イメージ

元のデータ:                     GROUP BY dept_id の結果:
emp_id | dept_id | salary dept_id | COUNT | AVG
-------+---------+-------- --------+-------+--------
1 | 1 | 350000 → 1 | 3 | 330000
5 | 1 | 330000 2 | 3 | 340000
7 | 1 | 310000 3 | 2 | 320000
2 | 2 | 400000
3 | 2 | 320000
6 | 2 | 300000
4 | 3 | 280000
8 | 3 | 360000

SELECT句に書ける列の制限

重要

GROUP BYを使う場合、SELECT句に書けるのは以下の2種類だけである。

  1. GROUP BYで指定した列
  2. 集約関数
-- ✅ 正しい
SELECT dept_id, COUNT(*) FROM employees GROUP BY dept_id;

-- ❌ エラー!emp_nameはGROUP BYに含まれていない
SELECT dept_id, emp_name, COUNT(*) FROM employees GROUP BY dept_id;

GROUP BYでグループ化すると、各グループから1行だけ結果が出る。 emp_name はグループ内に複数の値があるため、どの値を返すか決められない。


Step 5: 複数列によるGROUP BY

複数の列でグループ化する

GROUP BYにカンマ区切りで複数の列を指定すると、 列の組み合わせ でグループ化できる。

CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER, hire_date DATE NOT NULL, salary INTEGER ); INSERT INTO employees VALUES (1, '田中太郎', 1, '2020-04-01', 350000); INSERT INTO employees VALUES (2, '佐藤花子', 2, '2021-04-01', 400000); INSERT INTO employees VALUES (3, '鈴木一郎', 2, '2022-04-01', 320000); INSERT INTO employees VALUES (4, '高橋美咲', 3, '2023-04-01', 280000); INSERT INTO employees VALUES (5, '伊藤健太', 1, '2021-04-01', 330000); INSERT INTO employees VALUES (6, '渡辺あかり', 2, '2023-04-01', 300000); INSERT INTO employees VALUES (7, '山本裕子', 1, '2022-04-01', 310000); INSERT INTO employees VALUES (8, '中村大輔', 3, '2020-04-01', 360000); -- 部門 × 入社年ごとの社員数 SELECT dept_id, EXTRACT(YEAR FROM hire_date) AS 入社年, COUNT(*) AS 社員数 FROM employees GROUP BY dept_id, EXTRACT(YEAR FROM hire_date) ORDER BY dept_id, 入社年;
補足

EXTRACT(YEAR FROM hire_date) は日付から年だけを取り出す関数である。 PostgreSQLでは DATE_PART('year', hire_date) と書くこともできる。


Step 6: HAVING句(集計結果の絞り込み)

HAVINGで集計結果を絞り込む

HAVING 句は、 GROUP BYで集計した結果 に対して条件を指定する。

SELECT グループ列, 集約関数()
FROM テーブル名
GROUP BY グループ列
HAVING 集約関数() の条件;
CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER, salary INTEGER ); INSERT INTO employees VALUES (1, '田中太郎', 1, 350000); INSERT INTO employees VALUES (2, '佐藤花子', 2, 400000); INSERT INTO employees VALUES (3, '鈴木一郎', 2, 320000); INSERT INTO employees VALUES (4, '高橋美咲', 3, 280000); INSERT INTO employees VALUES (5, '伊藤健太', 1, 330000); INSERT INTO employees VALUES (6, '渡辺あかり', 2, 300000); INSERT INTO employees VALUES (7, '山本裕子', 1, 310000); INSERT INTO employees VALUES (8, '中村大輔', 3, 360000); -- 社員が3人以上いる部門 SELECT dept_id, COUNT(*) AS 社員数 FROM employees GROUP BY dept_id HAVING COUNT(*) >= 3; -- 平均給与が33万以上の部門 SELECT dept_id, AVG(salary) AS 平均給与 FROM employees GROUP BY dept_id HAVING AVG(salary) >= 330000;

WHEREとHAVINGの違い

WHEREHAVING
タイミングGROUP BY に絞り込むGROUP BY に絞り込む
対象個々の行グループ(集計結果)
集約関数の使用使えない使える
CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER, salary INTEGER ); INSERT INTO employees VALUES (1, '田中太郎', 1, 350000); INSERT INTO employees VALUES (2, '佐藤花子', 2, 400000); INSERT INTO employees VALUES (3, '鈴木一郎', 2, 320000); INSERT INTO employees VALUES (4, '高橋美咲', 3, 280000); INSERT INTO employees VALUES (5, '伊藤健太', 1, 330000); INSERT INTO employees VALUES (6, '渡辺あかり', 2, 300000); INSERT INTO employees VALUES (7, '山本裕子', 1, 310000); INSERT INTO employees VALUES (8, '中村大輔', 3, 360000); -- WHEREとHAVINGの組み合わせ -- ① WHERE: 給与30万以上の社員だけを対象にする(グループ化前の絞り込み) -- ② GROUP BY: 部門ごとにグループ化 -- ③ HAVING: 社員数が2人以上の部門だけ表示(グループ化後の絞り込み) SELECT dept_id, COUNT(*) AS 社員数, AVG(salary) AS 平均給与 FROM employees WHERE salary >= 300000 GROUP BY dept_id HAVING COUNT(*) >= 2;

SQL文の実行順序

SQLは書いた順序とは異なる順序で実行される。

1. FROM     テーブルを選択
2. WHERE 行を絞り込む
3. GROUP BY グループ化する
4. HAVING グループを絞り込む
5. SELECT 列を選択する
6. ORDER BY 並び替える
7. LIMIT 件数を制限する
SQL文の実行順序を理解する

SELECTを最初に書くが、実際に最初に実行されるのはFROMである。 この実行順序を理解しておくと、「WHEREで集約関数が使えない理由」や 「HAVINGでGROUP BY後のデータにアクセスできる理由」が納得できる。


Step 7: AI活用のポイント

AIに集計SQLを書かせてみよう

集計の概念を理解していれば、AIへの指示が的確になる。

例1:

「部門ごとの平均給与を、平均給与の高い順に表示するSQLを書いて」

AIの回答:

SELECT dept_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY dept_id
ORDER BY avg_salary DESC;

例2:

「社員が3人以上いる部門の平均給与を求めるSQLを書いて」

AIの回答:

SELECT dept_id, COUNT(*) AS emp_count, AVG(salary) AS avg_salary
FROM employees
GROUP BY dept_id
HAVING COUNT(*) >= 3;

レビューのポイント

チェック項目確認内容
GROUP BYの列は正しいかグループ化の単位は要件通りか
SELECT句にGROUP BY以外の列がないか集約関数なしの列は書けない
WHEREとHAVINGの使い分けは正しいかグループ化前・後の条件を間違えていないか
NULLの扱いは考慮されているかNULLを含む列の集計で意図した結果になるか

Step 8: 実践課題

課題1:全社員の給与統計を求めよう

全社員の給与について、合計・平均・最高額・最低額をまとめて表示するSQLを書いてみよう。

課題2:部門ごとの社員数を求めよう

部門ごとの社員数を、多い順に表示するSQLを書いてみよう。

課題3:HAVINGで絞り込もう

社員が3人以上いる部門の平均給与を求めるSQLを書いてみよう。

課題4:入社年ごとの集計をしよう

入社年ごとの社員数を求めるSQLを書いてみよう。

ヒント

EXTRACT(YEAR FROM hire_date) で入社年を取り出し、それをGROUP BYに指定する。

CREATE TABLE departments ( dept_id INTEGER PRIMARY KEY, dept_name VARCHAR(50) NOT NULL UNIQUE, location VARCHAR(50) ); CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER REFERENCES departments(dept_id), hire_date DATE NOT NULL, salary INTEGER CHECK (salary > 0), email VARCHAR(100) UNIQUE ); INSERT INTO departments VALUES (1, '営業部', '東京'); INSERT INTO departments VALUES (2, '開発部', '大阪'); INSERT INTO departments VALUES (3, '人事部', '東京'); INSERT INTO employees VALUES (1, '田中太郎', 1, '2020-04-01', 350000, 'tanaka@example.com'); INSERT INTO employees VALUES (2, '佐藤花子', 2, '2021-04-01', 400000, 'sato@example.com'); INSERT INTO employees VALUES (3, '鈴木一郎', 2, '2022-04-01', 320000, 'suzuki@example.com'); INSERT INTO employees VALUES (4, '高橋美咲', 3, '2023-04-01', 280000, 'takahashi@example.com'); INSERT INTO employees VALUES (5, '伊藤健太', 1, '2021-10-01', 330000, 'ito@example.com'); INSERT INTO employees VALUES (6, '渡辺あかり', 2, '2023-07-01', 300000, 'watanabe@example.com'); INSERT INTO employees VALUES (7, '山本裕子', 1, '2022-04-01', 310000, 'yamamoto@example.com'); INSERT INTO employees VALUES (8, '中村大輔', 3, '2020-10-01', 360000, 'nakamura@example.com'); -- ここにSQLを書こう

まとめ

この章では、 データの集計 を学んだ。

🎯 達成できたこと

  • ✅ 集約関数(COUNT、SUM、AVG、MAX、MIN)を使えるようになった
  • ✅ GROUP BYでデータをグループ化して集計できるようになった
  • ✅ HAVING句で集計結果を絞り込めるようになった
  • ✅ WHEREとHAVINGの違いを説明できるようになった
  • ✅ NULLが集計に与える影響を理解した

📚 学んだ内容

構文意味
COUNT(*)行数を数えるSELECT COUNT(*) FROM employees
SUM(列)合計を求めるSELECT SUM(salary) FROM employees
AVG(列)平均を求めるSELECT AVG(salary) FROM employees
MAX(列) / MIN(列)最大値/最小値SELECT MAX(salary) FROM employees
GROUP BYグループ化GROUP BY dept_id
HAVING集計結果の絞り込みHAVING COUNT(*) >= 3

🚀 次のステップ

次の章では、 テーブルの分割と正規化 を学ぶ。 「なぜテーブルを分割するのか」を理解し、データベース設計の基礎を身につけよう。


💡 よくある質問

Q1: GROUP BYなしで集約関数を使うとどうなるか?

A: GROUP BYなしで集約関数を使うと、テーブル全体を1つのグループとして集計する。結果は常に1行になる。例えば SELECT COUNT(*) FROM employees はテーブル全体の行数を返す。

Q2: HAVINGの中でWHEREのような条件を書いてもよいか?

A: 技術的には可能だが、パフォーマンスの観点から グループ化前の絞り込みはWHERE、グループ化後の絞り込みはHAVING と使い分けるべきである。WHEREで先に行数を減らしてからGROUP BYしたほうが効率がよい。

Q3: GROUP BYの列をSELECT句に書かなくてもよいか?

A: 技術的には書かなくてもエラーにならない。しかし、GROUP BY列をSELECTに含めないと、どのグループの集計結果なのか区別できなくなる。通常は GROUP BY で指定した列を SELECT にも書く。

Q4: 集約関数を複数組み合わせて使えるか?

A: はい、1つのSELECT文で複数の集約関数を同時に使える。例:SELECT COUNT(*), AVG(salary), MAX(salary) FROM employees。ただし、集約関数の中に集約関数をネストすること(AVG(COUNT(*)))はできない。

Q5: COUNT(*)とCOUNT(1)の違いは何か?

A: 結果は同じで、どちらも全行数を返す。COUNT(*) は「全ての行」を数え、 COUNT(1) は「各行に1を割り当てて数える」。PostgreSQLではパフォーマンスの差はない。一般的には COUNT(*) が使われる。


練習問題

この章の内容を理解できたか確認しよう。

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

employees テーブルの全社員数を取得するSQL文を完成させよ。

SELECT
(*) AS 社員数 FROM employees;

解答例
SELECT COUNT(*) AS 社員数 FROM employees;
解説

COUNT(*) はテーブルの行数(NULLを含む全行)を数える集約関数である。

COUNT の種類

  • COUNT(*): 全行数を数える(NULLも含む)
  • COUNT(列名): その列がNULLでない行の数を数える

使用例

SELECT COUNT(*) FROM employees;        -- 全社員数
SELECT COUNT(dept_id) FROM employees;  -- dept_idがNULLでない社員数(部門所属者)
SELECT COUNT(DISTINCT dept_id) FROM employees;  -- 部門の種類数

ポイント

  • 集約関数は単独で使う場合、テーブル全体で1行の結果を返す
  • GROUP BY と組み合わせることでグループごとの件数を数えられる

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

salary の合計・平均・最大・最小を取得するSQL文の空欄を埋めよ。

SELECT SUM(salary) AS 合計,
(salary) AS 平均, MAX(salary) AS 最大,
(salary) AS 最小 FROM employees;

解答例
SELECT SUM(salary) AS 合計, AVG(salary) AS 平均, MAX(salary) AS 最大, MIN(salary) AS 最小 FROM employees;
解説

主な集約関数は SUM(合計)、AVG(平均)、MAX(最大値)、MIN(最小値)の4つである。

集約関数一覧

  • COUNT(*): 行数
  • SUM(列名): 合計値
  • AVG(列名): 平均値
  • MAX(列名): 最大値
  • MIN(列名): 最小値

NULLの扱い

  • SUM, AVG, MAX, MIN はすべてNULL値を無視して計算する
  • NULLを0として計算したい場合は COALESCE(salary, 0) でNULLを0に置換する

ポイント

  • 数値列だけでなく日付に MAX/MIN を使うと最新・最古の日付を取得できる

salary にNULLが入っている行が1件あるとき、AVG(salary) の結果について正しいものを選べ。 (employees テーブルには5行あり、4行に値があり1行がNULLである)

集約関数はNULLをどう扱うか考えよう

正解

B. NULL行を除いた行数で割った平均を返す

解説

集約関数(AVGSUMMAXMIN)はNULL値を自動的に無視して計算する。

具体例: 5人の社員の salary データ: 350000, 420000, 310000, NULL, 480000

  • COUNT(*) = 5(NULLの行も数える)
  • COUNT(salary) = 4(NULLを除いた行数)
  • AVG(salary) = (350000 + 420000 + 310000 + 480000) / 4 = 390000

選択肢Aのように5で割る(NULLを0として計算)のではなく、4で割ることに注意。

NULLを0として扱いたい場合

SELECT AVG(COALESCE(salary, 0)) FROM employees;
-- COALESCE(salary, 0) は salary が NULL なら 0 を返す

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

部門ごと(dept_id)の社員数を集計するSQL文を完成させよ。

SELECT dept_id, COUNT(*) AS 社員数 FROM employees
;

解答例
SELECT dept_id, COUNT(*) AS 社員数 FROM employees GROUP BY dept_id;
解説

GROUP BY 列名 で指定した列の値ごとにグループに分けて集計できる。

基本構文:

SELECT グループ列, 集約関数 FROM テーブル名 GROUP BY グループ列;

ポイント

  • GROUP BY を使うとき、SELECT 句に書ける列は以下のみ
    • GROUP BY に指定した列
    • 集約関数(COUNT, SUM, AVG, MAX, MIN
  • GROUP BY に指定していない列を SELECT に書くとエラーになる(標準SQLの場合)

実行イメージ

dept_id=1: 田中太郎, 中村健一  → COUNT(*)=2
dept_id=2: 鈴木花子, 佐藤次郎  → COUNT(*)=2
dept_id=NULL: 渡辺あかり      → COUNT(*)=1

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

部門ごと(dept_id)の平均給与を、平均給与の降順で取得するSQL文を完成させよ。

SELECT dept_id, AVG(salary) AS 平均給与 FROM employees
dept_id
AVG(salary)
;

GROUP BY の後に並び替えをするには ORDER BY を使う

解答例
SELECT dept_id, AVG(salary) AS 平均給与 FROM employees GROUP BY dept_id ORDER BY AVG(salary) DESC;
解説

GROUP BYORDER BY を組み合わせることで、グループ集計の結果を並び替えることができる。

ORDER BY での集約関数の使用

  • ORDER BY AVG(salary) DESC: 平均給与の降順で並べる
  • ORDER BY 平均給与 DESC: 別名を使って並べることも可能
  • ORDER BY 2 DESC: SELECT句の2番目の列を基準に並べる(列番号参照)

複数列での GROUP BY

GROUP BY dept_id, hire_date
-- dept_id と hire_date の組み合わせごとにグループ化する

実行順序FROMWHEREGROUP BYHAVINGSELECTORDER BYLIMIT

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

社員数が2人以上の部門のみを表示するSQL文を完成させよ。

SELECT dept_id, COUNT(*) AS 社員数 FROM employees GROUP BY dept_id
COUNT(*)
2;

集計後の絞り込みには WHERE ではなく別のキーワードを使う

解答例
SELECT dept_id, COUNT(*) AS 社員数 FROM employees GROUP BY dept_id HAVING COUNT(*) >= 2;
解説

HAVINGGROUP BY でグループ化した後の集計結果に対して条件を絞り込む句である。

基本構文:

SELECT グループ列, 集約関数
FROM テーブル名
GROUP BY グループ列
HAVING 集約条件;

WHERE との使い分け

  • WHERE: グループ化・集計の に行を絞り込む(集約関数は使えない)
  • HAVING: グループ化・集計の にグループを絞り込む(集約関数が使える)

よくある間違いWHERE COUNT(*) >= 2 ← エラー!集約関数は WHERE には書けない HAVING COUNT(*) >= 2 ← 正しい

WHERE 句と HAVING 句の違いとして正しいものを選べ。

正解

B. WHERE 句はグループ化前の行を絞り込み、HAVING 句はグループ化後の集計結果を絞り込む

解説

WHERE と HAVING の違い

項目WHEREHAVING
タイミングGROUP BY の前GROUP BY の後
対象個々の行グループ
集約関数使えない使える

書き方の順序

SELECT dept_id, COUNT(*)
FROM employees
WHERE salary > 200000    -- ①グループ化前に行を絞り込む
GROUP BY dept_id
HAVING COUNT(*) >= 2     -- ②グループ化後に集計結果を絞り込む
ORDER BY dept_id;

上の例では: ①まず salary > 200000 の社員のみに絞り込んでから ②部門ごとにグループ化し、2人以上の部門のみを表示する

WHERE COUNT(*) >= 2 と書くとエラーになる。

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

「部門ごとの最高給与と最低給与を取得し、最高給与が350000以上の部門のみ表示せよ」を実装するSQL文を完成させよ。

SELECT dept_id, MAX(salary) AS 最高給与,
(salary) AS 最低給与 FROM employees GROUP BY dept_id HAVING
(salary) >= 350000;

最大・最小それぞれの集約関数を思い出そう

解答例
SELECT dept_id, MAX(salary) AS 最高給与, MIN(salary) AS 最低給与 FROM employees GROUP BY dept_id HAVING MAX(salary) >= 350000;
解説

要件を読んで集計SQLに変換するときは、必要な集約関数と条件を整理する。

この問題の要件の分解

  • 「部門ごとの最高給与と最低給与を取得」 → SELECT dept_id, MAX(salary), MIN(salary) ... GROUP BY dept_id
  • 「最高給与が350000以上の部門のみ」 → HAVING MAX(salary) >= 350000

集約関数の選び方

  • 「最大」→ MAX
  • 「最小」→ MIN
  • 「合計」→ SUM
  • 「平均」→ AVG
  • 「件数」→ COUNT

要件を読んで適切な集約関数を選ぶことが重要である。