はじめに
【SQL実践ドリル】 シリーズ第2回のテーマは 集計関数と GROUP BY です。
データベースの真価は、大量のデータを瞬時に集計・分析できることにあります。「部署ごとの平均給与は?」「月別の売上件数は?」といった問いに答えるために、集計関数と GROUP BY は欠かせません。
対象読者
- SELECT の基本(第1回の内容)を理解している方
- データの集計・グループ化を学びたい方
- MySQL を使っている方(PostgreSQL との差異は都度補足します)
難易度の目安
| マーク | レベル | 説明 |
|---|---|---|
| ⭐ | 基本 | 集計関数・GROUP BY の基本 |
| ⭐⭐ | 応用 | HAVING や複数の集計関数を組み合わせる |
| ⭐⭐⭐ | チャレンジ | JOIN と集計を組み合わせた実践問題 |
サンプルデータベース
第1回と同じデータベースを使います。まだ作成していない方は、以下のSQL文を実行してください。
-- 部署テーブル
CREATE TABLE departments (
department_id INT PRIMARY KEY,
department_name VARCHAR(50) NOT NULL,
location VARCHAR(50)
);
INSERT INTO departments VALUES
(1, '営業部', '東京'),
(2, '開発部', '大阪'),
(3, '人事部', '東京'),
(4, '経理部', '名古屋'),
(5, 'マーケティング部', '福岡');
-- 社員テーブル
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
department_id INT,
hire_date DATE NOT NULL,
salary INT NOT NULL,
manager_id INT,
FOREIGN KEY (department_id) REFERENCES departments(department_id),
FOREIGN KEY (manager_id) REFERENCES employees(employee_id)
);
INSERT INTO employees VALUES
(1, '田中太郎', 1, '2020-04-01', 350000, NULL),
(2, '佐藤花子', 2, '2019-07-15', 420000, 1),
(3, '鈴木一郎', 1, '2021-01-10', 300000, 1),
(4, '高橋美咲', 3, '2018-09-01', 380000, NULL),
(5, '伊藤健太', 2, '2022-03-20', 450000, 2),
(6, '渡辺さくら', 2, '2023-06-01', 280000, 2),
(7, '山本大輔', 4, '2020-11-15', 330000, 4),
(8, '中村真理', 1, '2021-08-01', 310000, 1),
(9, '小林誠', 3, '2024-01-15', 270000, 4),
(10, '加藤優子', NULL, '2023-10-01', 290000, NULL);
-- 顧客テーブル
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
customer_name VARCHAR(100) NOT NULL,
email VARCHAR(100),
prefecture VARCHAR(20),
created_at DATE NOT NULL
);
INSERT INTO customers VALUES
(1, '株式会社ABC', 'abc@example.com', '東京都', '2023-01-15'),
(2, 'DEFコーポレーション', 'def@example.com', '大阪府', '2023-03-20'),
(3, 'GHI商事', 'ghi@example.com', '東京都', '2023-06-10'),
(4, 'JKLテクノロジー', NULL, '愛知県', '2024-01-05'),
(5, 'MNOサービス', 'mno@example.com', '福岡県', '2024-04-20');
-- 商品テーブル
CREATE TABLE products (
product_id INT PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
category VARCHAR(50) NOT NULL,
price INT NOT NULL,
stock INT NOT NULL DEFAULT 0
);
INSERT INTO products VALUES
(1, 'ノートPC Pro', 'パソコン', 198000, 50),
(2, 'ワイヤレスマウス', '周辺機器', 3500, 200),
(3, 'USBハブ 7ポート', '周辺機器', 4800, 150),
(4, '4Kモニター 27インチ', 'モニター', 45000, 30),
(5, 'メカニカルキーボード', '周辺機器', 12000, 80),
(6, 'ノートPC Light', 'パソコン', 89000, 100),
(7, 'Webカメラ HD', '周辺機器', 6500, 120),
(8, 'ゲーミングモニター', 'モニター', 65000, 20);
-- 注文テーブル
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT NOT NULL,
employee_id INT NOT NULL,
order_date DATE NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id),
FOREIGN KEY (employee_id) REFERENCES employees(employee_id)
);
INSERT INTO orders VALUES
(1, 1, 1, '2024-07-01'),
(2, 2, 3, '2024-07-05'),
(3, 1, 1, '2024-07-10'),
(4, 3, 8, '2024-08-01'),
(5, 4, 3, '2024-08-15'),
(6, 2, 1, '2024-09-01'),
(7, 5, 8, '2024-09-10'),
(8, 1, 3, '2024-10-01'),
(9, 3, 1, '2024-10-15'),
(10, 4, 8, '2024-11-01');
-- 注文明細テーブル
CREATE TABLE order_details (
detail_id INT PRIMARY KEY,
order_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(order_id),
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
INSERT INTO order_details VALUES
(1, 1, 1, 2),
(2, 1, 2, 5),
(3, 2, 4, 1),
(4, 2, 5, 3),
(5, 3, 6, 1),
(6, 3, 3, 2),
(7, 4, 1, 1),
(8, 4, 7, 4),
(9, 5, 2, 10),
(10, 5, 5, 2),
(11, 6, 8, 1),
(12, 6, 2, 3),
(13, 7, 6, 2),
(14, 7, 3, 5),
(15, 8, 1, 1),
(16, 8, 4, 2),
(17, 9, 5, 1),
(18, 9, 7, 3),
(19, 10, 6, 3),
(20, 10, 2, 8);
集計関数の基本
主な集計関数
| 関数 | 説明 | NULLの扱い |
|---|---|---|
COUNT(*) |
行数を数える | NULLを含む全行を数える |
COUNT(列名) |
指定列がNULLでない行数を数える | NULLを除外する |
SUM(列名) |
合計値を求める | NULLを無視する |
AVG(列名) |
平均値を求める | NULLを無視する(分母からも除外) |
MAX(列名) |
最大値を求める | NULLを無視する |
MIN(列名) |
最小値を求める | NULLを無視する |
-- 全社員の人数と平均給与
SELECT COUNT(*) AS 社員数, AVG(salary) AS 平均給与
FROM employees;
COUNT(*) と COUNT(列名) の違い
この違いは頻出の面接問題でもあります。
SELECT
COUNT(*) AS 全行数, -- 10(全社員)
COUNT(department_id) AS 部署あり人数, -- 9(NULLの加藤優子を除く)
COUNT(manager_id) AS 上司あり人数 -- 7(NULLの3名を除く)
FROM employees;
DISTINCT(重複排除)
集計関数の中で DISTINCT を使うと、重複する値を除外して集計できます。
-- 社員が所属している部署の数(NULLと重複を除く)
SELECT COUNT(DISTINCT department_id) AS 部署数
FROM employees;
-- 結果: 4(部署1, 2, 3, 4。NULLは除外される)
GROUP BY(グループ化)
GROUP BY は、指定した列の値が同じ行をグループにまとめ、各グループに対して集計関数を適用します。
SELECT department_id, COUNT(*) AS 人数
FROM employees
GROUP BY department_id;
重要なルール: SELECT に指定できるのは、以下のいずれかのみです。
-
GROUP BYに指定した列 - 集計関数(
COUNT,SUM,AVG,MAX,MIN)
-- NG: name は GROUP BY に指定していないため不正
SELECT department_id, name, COUNT(*)
FROM employees
GROUP BY department_id;
MySQL の注意点: MySQL はデフォルト(
sql_modeにONLY_FULL_GROUP_BYが含まれる場合)でこのルールを厳密にチェックします。ONLY_FULL_GROUP_BYが無効な環境ではエラーにならず不定な値が返されることがあるため、注意してください。
HAVING(グループの絞り込み)
HAVING は GROUP BY の結果に対する絞り込み条件です。WHERE が個々の行に対する条件であるのに対し、HAVING はグループ化後の結果に対する条件です。
-- 3人以上いる部署だけを表示
SELECT department_id, COUNT(*) AS 人数
FROM employees
GROUP BY department_id
HAVING COUNT(*) >= 3;
SQL の実行順序
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
この順序を理解しておくと、なぜ WHERE で集計関数を使えないのか(WHERE は GROUP BY より前に実行されるから)が分かります。
問題
問題 1 ⭐(基本):社員の総数を求める(COUNT)
社員テーブル (employees) に登録されている社員の総数を取得してください。列名は 社員数 としてください。
期待結果:
+--------+
| 社員数 |
+--------+
| 10 |
+--------+
1 row in set
模範解答
SELECT COUNT(*) AS 社員数
FROM employees;
解説: COUNT(*) はテーブルの全行数を返します。AS 社員数 は列の別名(エイリアス)を指定しています。COUNT(employee_id) でも同じ結果が得られますが、COUNT(*) のほうが「全行を数える」意図が明確です。
問題 2 ⭐(基本):全社員の平均給与(AVG)
全社員の平均給与を取得してください。列名は 平均給与 としてください。
期待結果:
+-----------+
| 平均給与 |
+-----------+
| 338000.00 |
+-----------+
1 row in set
計算過程: (350000 + 420000 + 300000 + 380000 + 450000 + 280000 + 330000 + 310000 + 270000 + 290000) / 10 = 3380000 / 10 = 338000
模範解答
SELECT AVG(salary) AS 平均給与
FROM employees;
解説: AVG(salary) は salary 列の平均値を返します。MySQLでは AVG の結果は DECIMAL 型で返されるため、小数点以下も表示されます。整数で欲しい場合は ROUND(AVG(salary), 0) や CAST(AVG(salary) AS SIGNED) を使います。
注意:
AVGはNULL値を無視して計算します。例えば、3行のうち1行がNULLなら分母は 2 です。salary列にNULLはないため、ここでは影響しません。
問題 3 ⭐(基本):部署ごとの社員数(GROUP BY)
部署ごとの社員数を取得してください。department_id と社員数を表示し、列名は 人数 としてください。
期待結果:
+---------------+------+
| department_id | 人数 |
+---------------+------+
| 1 | 3 |
| 2 | 3 |
| 3 | 2 |
| 4 | 1 |
| NULL | 1 |
+---------------+------+
5 rows in set
検算:
- 部署1(営業部): 田中太郎, 鈴木一郎, 中村真理 = 3名
- 部署2(開発部): 佐藤花子, 伊藤健太, 渡辺さくら = 3名
- 部署3(人事部): 高橋美咲, 小林誠 = 2名
- 部署4(経理部): 山本大輔 = 1名
- NULL: 加藤優子 = 1名
模範解答
SELECT department_id, COUNT(*) AS 人数
FROM employees
GROUP BY department_id;
解説: GROUP BY department_id により、department_id の値が同じ行がグループ化されます。department_id が NULL の行も1つのグループとしてまとめられます。
補足:
GROUP BYの結果の並び順はSQL標準では保証されていません。特定の順序が必要な場合はORDER BYを追加してください。
問題 4 ⭐⭐(応用):部署ごとの平均給与(GROUP BY + AVG)
部署ごとの平均給与を取得してください。department_id と平均給与を表示し、平均給与の列名は 平均給与 としてください。平均給与の高い順に並べてください。department_id が NULL の社員は除外してください。
期待結果:
+---------------+------------+
| department_id | 平均給与 |
+---------------+------------+
| 2 | 383333.33 |
| 4 | 330000.00 |
| 3 | 325000.00 |
| 1 | 320000.00 |
+---------------+------------+
4 rows in set
検算:
- 部署1: (350000 + 300000 + 310000) / 3 = 960000 / 3 = 320000
- 部署2: (420000 + 450000 + 280000) / 3 = 1150000 / 3 = 383333.33...
- 部署3: (380000 + 270000) / 2 = 650000 / 2 = 325000
- 部署4: 330000 / 1 = 330000
模範解答
SELECT department_id, AVG(salary) AS 平均給与
FROM employees
WHERE department_id IS NOT NULL
GROUP BY department_id
ORDER BY 平均給与 DESC;
解説: WHERE department_id IS NOT NULL で部署未所属の社員を除外してから GROUP BY でグループ化しています。WHERE は GROUP BY より前に実行されるため、グループ化の前に行を絞り込めます。
ORDER BY では SELECT で定義した別名 平均給与 を使えます。これは ORDER BY が SELECT より後に実行されるためです。
PostgreSQL との違い: 表示上の差はありますが、
AVGの返り値の精度はDBMSによって異なります。PostgreSQLではnumeric型で返されます。
問題 5 ⭐⭐(応用):カテゴリ別の商品数と平均価格
商品テーブル (products) から、カテゴリ別の商品数と平均価格を取得してください。列名はそれぞれ 商品数 と 平均価格 としてください。
期待結果:
+------------+--------+----------+
| category | 商品数 | 平均価格 |
+------------+--------+----------+
| パソコン | 2 | 143500.00|
| モニター | 2 | 55000.00|
| 周辺機器 | 4 | 6700.00|
+------------+--------+----------+
3 rows in set
検算:
- パソコン: ノートPC Pro (198000), ノートPC Light (89000) → 2件, 平均 (198000+89000)/2 = 143500
- モニター: 4Kモニター 27インチ (45000), ゲーミングモニター (65000) → 2件, 平均 (45000+65000)/2 = 55000
- 周辺機器: ワイヤレスマウス (3500), USBハブ 7ポート (4800), メカニカルキーボード (12000), Webカメラ HD (6500) → 4件, 平均 (3500+4800+12000+6500)/4 = 26800/4 = 6700
模範解答
SELECT
category,
COUNT(*) AS 商品数,
AVG(price) AS 平均価格
FROM products
GROUP BY category;
解説: 1つの GROUP BY に対して複数の集計関数を同時に適用できます。COUNT(*) と AVG(price) をカンマ区切りで並べるだけです。
実務では ROUND(AVG(price), 0) のように丸めて表示することも多いです。
問題 6 ⭐⭐(応用):3人以上の社員がいる部署(HAVING)
社員が3人以上所属している部署の department_id と社員数を取得してください。列名は 人数 としてください。
期待結果:
+---------------+------+
| department_id | 人数 |
+---------------+------+
| 1 | 3 |
| 2 | 3 |
+---------------+------+
2 rows in set
検算:
- 部署1: 3名(田中太郎, 鈴木一郎, 中村真理)→ 3 >= 3 なので該当
- 部署2: 3名(佐藤花子, 伊藤健太, 渡辺さくら)→ 3 >= 3 なので該当
- 部署3: 2名 → 不該当
- 部署4: 1名 → 不該当
- NULL: 1名 → 不該当
模範解答
SELECT department_id, COUNT(*) AS 人数
FROM employees
GROUP BY department_id
HAVING COUNT(*) >= 3;
解説: HAVING は GROUP BY の結果に対する条件です。WHERE ではなく HAVING を使う理由は、COUNT(*) はグループ化した後に計算される集計値であり、WHERE(グループ化前に実行)では参照できないためです。
-- NG: WHERE で集計関数は使えない
SELECT department_id, COUNT(*) AS 人数
FROM employees
WHERE COUNT(*) >= 3 -- エラー!
GROUP BY department_id;
HAVING 句で別名 人数 を使えるかはDBMSによって異なります。MySQLでは HAVING 人数 >= 3 と書くことも可能ですが、SQL標準に準拠するなら HAVING COUNT(*) >= 3 と書くのが確実です。
問題 7 ⭐⭐(応用):月別の注文件数(DATE関数 + GROUP BY)
注文テーブル (orders) から、月別の注文件数を取得してください。YEAR(order_date) と MONTH(order_date) でグループ化し、年月の昇順で並べてください。列名はそれぞれ 年、月、件数 としてください。
期待結果:
+------+------+------+
| 年 | 月 | 件数 |
+------+------+------+
| 2024 | 7 | 3 |
| 2024 | 8 | 2 |
| 2024 | 9 | 2 |
| 2024 | 10 | 2 |
| 2024 | 11 | 1 |
+------+------+------+
5 rows in set
検算:
- 2024年7月: 注文1 (7/1), 注文2 (7/5), 注文3 (7/10) = 3件
- 2024年8月: 注文4 (8/1), 注文5 (8/15) = 2件
- 2024年9月: 注文6 (9/1), 注文7 (9/10) = 2件
- 2024年10月: 注文8 (10/1), 注文9 (10/15) = 2件
- 2024年11月: 注文10 (11/1) = 1件
模範解答
SELECT
YEAR(order_date) AS 年,
MONTH(order_date) AS 月,
COUNT(*) AS 件数
FROM orders
GROUP BY YEAR(order_date), MONTH(order_date)
ORDER BY 年 ASC, 月 ASC;
解説: YEAR() と MONTH() はMySQLの日付関数で、DATE型の値から年・月を抽出します。GROUP BY には SELECT と同じ式を指定します。
別の書き方として DATE_FORMAT を使う方法もあります。
SELECT
DATE_FORMAT(order_date, '%Y-%m') AS 年月,
COUNT(*) AS 件数
FROM orders
GROUP BY DATE_FORMAT(order_date, '%Y-%m')
ORDER BY 年月 ASC;
PostgreSQL との違い: PostgreSQLでは
YEAR()/MONTH()関数がありません。代わりにEXTRACT(YEAR FROM order_date)やDATE_TRUNC('month', order_date)を使います。
問題 8 ⭐⭐⭐(チャレンジ):部署ごとの最高給与と最低給与の差
各部署について、最高給与と最低給与の差額を求めてください。department_id、最高給与、最低給与、差額を表示してください。列名はそれぞれ 最高給与、最低給与、給与差 としてください。差額の大きい順に並べ、department_id が NULL の社員は除外してください。
期待結果:
+---------------+----------+----------+--------+
| department_id | 最高給与 | 最低給与 | 給与差 |
+---------------+----------+----------+--------+
| 2 | 450000 | 280000 | 170000 |
| 3 | 380000 | 270000 | 110000 |
| 1 | 350000 | 300000 | 50000 |
| 4 | 330000 | 330000 | 0 |
+---------------+----------+----------+--------+
4 rows in set
検算:
- 部署1: MAX=350000(田中), MIN=300000(鈴木), 差=50000
- 部署2: MAX=450000(伊藤), MIN=280000(渡辺), 差=170000
- 部署3: MAX=380000(高橋), MIN=270000(小林), 差=110000
- 部署4: MAX=330000(山本), MIN=330000(山本), 差=0(1人のため同値)
模範解答
SELECT
department_id,
MAX(salary) AS 最高給与,
MIN(salary) AS 最低給与,
MAX(salary) - MIN(salary) AS 給与差
FROM employees
WHERE department_id IS NOT NULL
GROUP BY department_id
ORDER BY 給与差 DESC;
解説: MAX(salary) - MIN(salary) のように、集計関数の結果に対して算術演算を行えます。SELECT 句では集計関数どうしの計算が可能です。
部署4(経理部)は山本大輔さん1人だけなので、MAX と MIN が同じ値になり差は 0 です。
問題 9 ⭐⭐⭐(チャレンジ):注文金額の合計が100,000円以上の注文
注文ごとの合計金額を求め、合計金額が100,000円以上の注文だけを抽出してください。注文テーブル (orders) と注文明細テーブル (order_details) と商品テーブル (products) を結合し、注文ごとに quantity * price の合計を計算します。order_id と合計金額を表示し、列名は 合計金額 としてください。合計金額の降順で並べてください。
期待結果:
+----------+----------+
| order_id | 合計金額 |
+----------+----------+
| 1 | 413500 |
| 10 | 295000 |
| 8 | 288000 |
| 4 | 224000 |
| 7 | 202000 |
+----------+----------+
5 rows in set
検算(全注文の合計金額):
- 注文1: 2 x 198000 (ノートPC Pro) + 5 x 3500 (ワイヤレスマウス) = 396000 + 17500 = 413500
- 注文2: 1 x 45000 (4Kモニター) + 3 x 12000 (メカニカルキーボード) = 45000 + 36000 = 81000
- 注文3: 1 x 89000 (ノートPC Light) + 2 x 4800 (USBハブ) = 89000 + 9600 = 98600
- 注文4: 1 x 198000 (ノートPC Pro) + 4 x 6500 (Webカメラ) = 198000 + 26000 = 224000
- 注文5: 10 x 3500 (ワイヤレスマウス) + 2 x 12000 (メカニカルキーボード) = 35000 + 24000 = 59000
- 注文6: 1 x 65000 (ゲーミングモニター) + 3 x 3500 (ワイヤレスマウス) = 65000 + 10500 = 75500
- 注文7: 2 x 89000 (ノートPC Light) + 5 x 4800 (USBハブ) = 178000 + 24000 = 202000
- 注文8: 1 x 198000 (ノートPC Pro) + 2 x 45000 (4Kモニター) = 198000 + 90000 = 288000
- 注文9: 1 x 12000 (メカニカルキーボード) + 3 x 6500 (Webカメラ) = 12000 + 19500 = 31500
- 注文10: 3 x 89000 (ノートPC Light) + 8 x 3500 (ワイヤレスマウス) = 267000 + 28000 = 295000
100,000円以上: 注文1 (413500), 注文4 (224000), 注文7 (202000), 注文8 (288000), 注文10 (295000)
模範解答
SELECT
od.order_id,
SUM(od.quantity * p.price) AS 合計金額
FROM order_details od
JOIN products p ON od.product_id = p.product_id
GROUP BY od.order_id
HAVING SUM(od.quantity * p.price) >= 100000
ORDER BY 合計金額 DESC;
解説: この問題では JOIN、GROUP BY、HAVING を組み合わせています。
-
order_detailsとproductsをproduct_idで結合して、各明細行の金額(quantity * price)を計算できるようにする -
GROUP BY od.order_idで注文ごとにグループ化する -
SUM(od.quantity * p.price)で注文ごとの合計金額を算出する -
HAVINGで合計金額が100,000円以上のグループだけを抽出する
HAVING の条件に別名 合計金額 を使えるかは DBMS によります。MySQL では使用可能ですが、移植性を考慮するなら HAVING SUM(od.quantity * p.price) >= 100000 と書くのが確実です。
問題 10 ⭐⭐⭐(チャレンジ):顧客ごとの累計注文金額ランキング
顧客ごとの累計注文金額を求め、金額の高い順に並べてください。顧客テーブル (customers)、注文テーブル (orders)、注文明細テーブル (order_details)、商品テーブル (products) を結合して計算してください。customer_name(顧客名)と 累計金額 を表示してください。
期待結果:
+-------------------------+----------+
| customer_name | 累計金額 |
+-------------------------+----------+
| 株式会社ABC | 800100 |
| JKLテクノロジー | 354000 |
| GHI商事 | 255500 |
| MNOサービス | 202000 |
| DEFコーポレーション | 156500 |
+-------------------------+----------+
5 rows in set
検算:
- 株式会社ABC (customer_id=1): 注文1 (413500) + 注文3 (98600) + 注文8 (288000) = 800100
- DEFコーポレーション (customer_id=2): 注文2 (81000) + 注文6 (75500) = 156500
- GHI商事 (customer_id=3): 注文4 (224000) + 注文9 (31500) = 255500
- JKLテクノロジー (customer_id=4): 注文5 (59000) + 注文10 (295000) = 354000
- MNOサービス (customer_id=5): 注文7 (202000) = 202000
模範解答
SELECT
c.customer_name,
SUM(od.quantity * p.price) AS 累計金額
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
JOIN order_details od ON o.order_id = od.order_id
JOIN products p ON od.product_id = p.product_id
GROUP BY c.customer_id, c.customer_name
ORDER BY 累計金額 DESC;
解説: 4つのテーブルを連鎖的に結合しています。
customers → orders → order_details → products
結合の流れ:
-
customersとordersをcustomer_idで結合(どの顧客がどの注文をしたか) -
ordersとorder_detailsをorder_idで結合(各注文にどの明細があるか) -
order_detailsとproductsをproduct_idで結合(各明細の商品の単価を取得)
GROUP BY には customer_id を指定して顧客ごとにグループ化しています。customer_name も GROUP BY に含めるのは、SELECT に指定しているためです(MySQL の ONLY_FULL_GROUP_BY モードで必要)。customer_id が主キーであるため customer_name は関数従属しますが、明示的に含めておくのが安全です。
まとめ
第2回では、集計関数と GROUP BY を使ったデータの集計・分析を学びました。
| 構文 | 用途 |
|---|---|
COUNT(*) / COUNT(列名)
|
行数を数える |
SUM(列名) |
合計値を求める |
AVG(列名) |
平均値を求める |
MAX(列名) / MIN(列名)
|
最大値・最小値を求める |
DISTINCT |
重複を排除する |
GROUP BY 列名 |
指定列でグループ化する |
HAVING 条件 |
グループ化後の絞り込み |
重要なポイント:
-
WHERE vs HAVING:
WHEREは行単位の絞り込み(GROUP BY の前)、HAVINGはグループ単位の絞り込み(GROUP BY の後) -
NULL の扱い: 集計関数は
NULLを無視する(COUNT(*)を除く) -
GROUP BY のルール:
SELECTに書ける列は、GROUP BYに含まれる列か集計関数のみ
次回(第3回)は テーブル結合 (JOIN) を扱います。複数のテーブルからデータを自在に取得する方法を学びましょう。
参考
- MySQL 8.0 リファレンスマニュアル - 集約関数
- MySQL 8.0 リファレンスマニュアル - GROUP BY の扱い
- MySQL 8.0 リファレンスマニュアル - 日付と時刻関数
- PostgreSQL 16 ドキュメント - 集約関数
@kotaro_ai_lab
AI活用や開発効率化について発信しています。フォローお気軽にどうぞ!