#07 MySQLデータベースを使ってみよう

集計する — GROUP BY

集計とは

データベースには大量のレコードが蓄積されます。「カテゴリ別の商品数は?」「ユーザーごとの注文合計は?」といった集計を行うには、 GROUP BY? 指定した列の値が同じ行をまとめてグループ化する句。COUNT・SUM・AVGなどの集計関数と組み合わせて使う。「カテゴリ別の件数を集計」などに使用する。 集計関数? グループや全行に対して計算を行う関数。COUNT(件数)・SUM(合計)・AVG(平均)・MAX(最大)・MIN(最小)がある。GROUP BY と組み合わせて使うことが多い。 関数を使います。

GROUP BY がデータをグループ化して集計する仕組み 元データ(ordersテーブル) category total 電子機器 89800 電子機器 54000 電子機器 45000 PC周辺機器 12000 PC周辺機器 3200 PC周辺機器 2500 家電 35000 家電 8000 SELECT category, SUM(total) FROM orders GROUP BY category; GROUP BY 集計処理 集計結果 category SUM(total) COUNT(*) 電子機器 188800 3件 89800 + 54000 + 45000 ← 3行が1行に集約された PC周辺機器 17700 3件 12000 + 3200 + 2500 ← 3行が1行に集約された 家電 43000 2件 35000 + 8000 ← 2行が1行に集約された HAVING で集計後に絞り込み HAVING SUM(total) >= 30000 → 電子機器・家電のみ返す
図1: GROUP BYがデータをグループ化して集計するイメージ

サンプルデータ

CREATE TABLE orders (
  id         INT NOT NULL AUTO_INCREMENT,
  user_id    INT NOT NULL,
  category   VARCHAR(50) NOT NULL,
  product    VARCHAR(200) NOT NULL,
  total      DECIMAL(10, 2) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);

INSERT INTO orders (user_id, category, product, total) VALUES
  (1, '電子機器', 'ノートPC',    89800),
  (1, 'PC周辺機器', 'マウス',     3200),
  (2, 'PC周辺機器', 'キーボード', 12000),
  (2, '電子機器', 'タブレット',  54000),
  (3, '電子機器', 'モニター',    45000),
  (3, 'PC周辺機器', 'USBハブ',    2500),
  (4, '家電',     '電気ケトル',  8000),
  (4, '家電',     '空気清浄機',  35000);

GROUP BY — グループ化して集計

GROUP BY は指定したカラムの値が同じ行をひとまとめにします。

-- カテゴリ別の注文件数
SELECT category, COUNT(*) AS 注文件数
FROM orders
GROUP BY category;
+----------------+----------+
| category       | 注文件数 |
+----------------+----------+
| 電子機器       |        3 |
| PC周辺機器     |        3 |
| 家電           |        2 |
+----------------+----------+

集計関数

集計関数: COUNT / SUM / AVG / MAX / MIN サンプルデータ user_id total 1 89800 1 3200 2 12000 2 54000 3 45000 3 2500 COUNT() 行数を数える COUNT(*) → 6 COUNT(DISTINCT user_id) → 3 SUM() 合計を求める SUM(total) → 206500 売上合計・注文金額合計 などに使う AVG() 平均を求める AVG(total) ≈ 34416 206500 ÷ 6 = 34416.6 MAX() 最大値を求める MAX(total) → 89800 最も大きい注文金額 MIN() 最小値を求める MIN(total) → 2500 最も小さい注文金額 まとめて使う例 SELECT COUNT(*) AS 件数, SUM(total) AS 合計, AVG(total) AS 平均, MAX(total) AS 最大, MIN(total) AS 最小 NULLの扱い COUNT(*) NULLの行も数える COUNT(カラム名) NULLは除外して数える SUM/AVG/MAX/MINも NULL は無視される GROUP BY と組み合わせることで「カテゴリ別集計」「ユーザー別集計」などが実現できる
図2: COUNT・SUM・AVG・MAX・MINの動作と用途

COUNT — 件数を数える

-- 全注文件数
SELECT COUNT(*) AS 総注文数 FROM orders;

-- NULL を除いたカラムの件数(*との違い)
SELECT COUNT(user_id) AS ユーザーID件数 FROM orders;

-- ユニークな値の数
SELECT COUNT(DISTINCT user_id) AS ユニークユーザー数 FROM orders;

SUM — 合計を求める

-- カテゴリ別の売上合計
SELECT category, SUM(total) AS 売上合計
FROM orders
GROUP BY category
ORDER BY 売上合計 DESC;

AVG — 平均を求める

-- ユーザー別の平均注文金額
SELECT user_id, AVG(total) AS 平均注文金額
FROM orders
GROUP BY user_id;

MAX / MIN — 最大・最小を求める

-- カテゴリ別の最高・最低価格
SELECT
  category,
  MAX(total) AS 最高金額,
  MIN(total) AS 最低金額,
  COUNT(*)   AS 件数
FROM orders
GROUP BY category;

HAVING — 集計後に絞り込む

WHERE は集計前の行に条件をかけますが、集計後の結果に条件をかけるには HAVING を使います。

-- 売上合計が20000円以上のカテゴリのみ表示
SELECT category, SUM(total) AS 売上合計
FROM orders
GROUP BY category
HAVING SUM(total) >= 20000;
+------------+----------+
| category   | 売上合計 |
+------------+----------+
| 電子機器   | 188800   |
| PC周辺機器 |  17700   |
| 家電       |  43000   |
+------------+----------+

WHERE vs HAVING の使い分け

-- WHERE: 集計「前」に絞り込む(高速)
-- HAVING: 集計「後」に絞り込む

-- ❌ HAVINGで書けるがWHEREより遅い
SELECT category, SUM(total)
FROM orders
GROUP BY category
HAVING category = '電子機器';

-- ✅ カテゴリの絞り込みはWHEREで先に行う
SELECT category, SUM(total)
FROM orders
WHERE category = '電子機器'
GROUP BY category;

両方を使う例

-- アクティブユーザー(user_id >= 2)の中で
-- 合計購入金額が30000円以上のユーザーを取得
SELECT user_id, SUM(total) AS 合計購入金額
FROM orders
WHERE user_id >= 2        -- 集計「前」に絞り込む(WHERE)
GROUP BY user_id
HAVING SUM(total) >= 30000  -- 集計「後」に絞り込む(HAVING)
ORDER BY 合計購入金額 DESC;

GROUP BY + JOIN の組み合わせ

-- ユーザー名と注文集計を一緒に取得
SELECT
  u.name,
  COUNT(o.id)    AS 注文件数,
  SUM(o.total)   AS 合計金額,
  AVG(o.total)   AS 平均金額
FROM users AS u
LEFT JOIN orders AS o ON o.user_id = u.id
GROUP BY u.id, u.name
ORDER BY 合計金額 DESC;

WITH ROLLUP — 小計・合計行を追加する

WITH ROLLUP を使うと、グループごとの小計や全体合計行が自動で追加されます。

SELECT category, SUM(total) AS 売上合計
FROM orders
GROUP BY category WITH ROLLUP;
+----------------+----------+
| category       | 売上合計 |
+----------------+----------+
| PC周辺機器     |  17700   |
| 家電           |  43000   |
| 電子機器       | 188800   |
| NULL           | 249500   |  ← 全体合計
+----------------+----------+

最後の NULL の行が全体合計です。COALESCE を使って見た目を整えることもできます。

SELECT
  COALESCE(category, '【全体合計】') AS カテゴリ,
  SUM(total) AS 売上合計
FROM orders
GROUP BY category WITH ROLLUP;

まとめ

  • GROUP BY で同じ値の行をグループ化して集計する
  • COUNT(*): 件数、SUM(): 合計、AVG(): 平均、MAX()/MIN(): 最大/最小
  • WHERE は集計前の行を絞り込む(高速)
  • HAVING は集計後の結果を絞り込む
  • WITH ROLLUP で小計・合計行を自動追加できる

次回はバックアップ取得とユーザー管理でシリーズを締めくくります。