SQL / LESSON 07 / SQL07

SQL:GROUP BY

1学習の目的

2基礎解説

前章の集計はテーブル全体で1行でした。GROUP BY を付けると、グループごとに1行ずつ集計されます。

やりたいこと書き方結果
全体を集計SELECT COUNT(*) FROM products1行
グループ別に集計GROUP BY categoryカテゴリの数だけ行
並べ替えるGROUP BY … ORDER BY 合計 DESC集計値で並べ替え
絞ってから集計WHERE … GROUP BY …WHERE が先
✅ 覚えるべき重要ポイント
  1. SELECT に書けるのは「グループ化した列」と「集計関数」だけGROUP BY category のときに SELECT name と書くと、1グループに複数の名前があるため意味をなさない。
  2. NULLは1つのグループにまとまる。「値が入っていない」という共通点でグループ化され、独立した行として現れる。
  3. 句の順番は SELECT → FROM → WHERE → GROUP BY → ORDER BY → LIMITWHERE はグループ化の前に効く(行を減らしてから集計する)。
  4. ORDER BY には集計結果の別名も使えるORDER BY 合計 DESC で「売上の多いカテゴリ順」が作れる。
  5. グループ化の基準は複数指定できるし、計算式や CASE 式も使える。切り口を自由に設計できるのが GROUP BY の強み。

現場使用例:カテゴリ別売上、月別の注文件数、担当者ごとの成約数、地域別の会員数。Excelのピボットテーブルに相当する処理で、レポート作成の中心になる。

-- カテゴリごとの件数
SELECT category, COUNT(*) AS 件数
FROM products
GROUP BY category;
-- → NULL / PC / 周辺機器 の3行

-- 複数の集計をまとめて
SELECT
  category,
  COUNT(*)   AS 件数,
  SUM(price) AS 合計,
  AVG(price) AS 平均
FROM products
GROUP BY category;

-- 集計値で並べ替える
SELECT category, SUM(price) AS 合計
FROM products
GROUP BY category
ORDER BY 合計 DESC;

-- 絞ってから集計する(WHERE が先)
SELECT category, COUNT(*) AS 件数
FROM products
WHERE price IS NOT NULL
GROUP BY category;

-- NULLに名前を付けてからグループ化
SELECT COALESCE(category, '未分類') AS 分類, COUNT(*) AS 件数
FROM products
GROUP BY 分類;

3基本ドリル(10問)

SQL07-D01 category ごとの件数を、category と件数(別名「件数」)で取り出すSQLを書け。 コーディング★☆☆無料
期待される結果

3行。NULL 1 / PC 2 / 周辺機器 4

ヒント

GROUP BY category を FROM の後ろに付ける。

模範解答
SELECT category, COUNT(*) AS 件数
FROM products
GROUP BY category;
解説

グループの数だけ行が返る。前章の集計は全体で1行だったが、GROUP BY を付けると切り口ごとの一覧になる。

SQL07-D02 GROUP BY category としたとき、結果は何行になるか(categoryは NULL・PC・周辺機器 の3種類)。
A. 1行 B. 3行 C. 7行 D. 2行
選択★☆☆無料
期待される結果

解答 == B

ヒント

グループの数だけ返る。NULLも1つのグループ。

模範解答
B
解説

NULLも独立した1グループになる。「値が入っていない」という共通点でまとめられ、行として現れる。

SQL07-D03 category ごとの price の合計を、category と合計(別名「合計」)で取り出すSQLを書け。 コーディング★☆☆無料
期待される結果

3行。NULL 25000 / PC 173000 / 周辺機器 13200

ヒント

COUNT(*) の代わりに SUM(price) を使う。

模範解答
SELECT category, SUM(price) AS 合計
FROM products
GROUP BY category;
解説

グループごとに合計が計算される。周辺機器は4件あるが、priceがNULLの1件は合計から除かれている(SQL06)。

SQL07-D04 カテゴリごとの平均単価を取り出すSQLを完成させよ。 穴埋め★☆☆無料
コード
SELECT category, AVG(price) AS 平均
FROM products
____ category;
期待される結果

3行。NULL 25000.0 / PC 86500.0 / 周辺機器 4400.0

ヒント

「〜でグループ化する」を意味する2語。

模範解答
GROUP BY
解説

GROUP BY はグループ化の基準を指定する。この列の値が同じ行どうしが、1つのグループにまとめられる。

SQL07-D05 GROUP BY category のとき、SELECT に書いてよいのはどれか。
A. name B. COUNT(*) C. price D. stock
選択★☆☆無料
期待される結果

解答 == B

ヒント

1グループに複数の値があるものは書けない。

模範解答
B
解説

グループ化した列と集計関数だけが書ける。周辺機器グループには4つの名前があるので、name と書いてもどれを表示すべきか決まらない。
⚠ SQLiteだけは例外的にエラーにならず、グループ内のどれか1つが返る(どれが返るかは保証されない)。MySQL・PostgreSQLなど多くの環境ではエラーになるので、書かないのが正解

SQL07-D06 category ごとに、件数(別名「件数」)と合計(別名「合計」)と平均(別名「平均」)をまとめて取り出すSQLを書け。 コーディング★★☆無料
期待される結果

4列3行。PC は 2 / 173000 / 86500.0

ヒント

集計関数をカンマで並べる。GROUP BY は1回でよい。

模範解答
SELECT
  category,
  COUNT(*) AS 件数,
  SUM(price) AS 合計,
  AVG(price) AS 平均
FROM products
GROUP BY category;
解説

1回のグループ化で複数の集計が取れる。件数・合計・平均を別々に問い合わせる必要はない。

SQL07-D07 カテゴリ別の合計を、合計の大きい順に並べるSQLを完成させよ。 穴埋め★★☆無料
コード
SELECT category, SUM(price) AS 合計
FROM products
GROUP BY category
____ 合計 DESC;
期待される結果

3行。PC 173000 が先頭

ヒント

並べ替えを指示する2語。GROUP BY の後ろに書く。

模範解答
ORDER BY
解説

ORDER BY には集計結果の別名が使える。「売上の多い順」というレポートの基本形が、この1行で作れる。

SQL07-D08 句の正しい順番はどれか。
A. GROUP BY → WHERE B. WHERE → GROUP BY C. 順番は自由 D. 両方は使えない
選択★★☆無料
期待される結果

解答 == B

ヒント

行を減らしてから集計するのか、集計してから減らすのか。

模範解答
B
解説

WHERE は集計する前に効く。まず条件で行を絞り、残った行をグループ化する。集計した「後」に絞りたい場合は、次章の HAVING を使う。

SQL07-D09 category ごとの在庫金額(price × stock の合計)を、category と在庫金額(別名「在庫額」)で取り出すSQLを書け。 コーディング★★☆無料
期待される結果

3行。PC 955000 / 周辺機器 467400 / NULLは空

ヒント

SUM(price * stock) と書く。集計関数の中に計算式を入れられる。

模範解答
SELECT category, SUM(price * stock) AS 在庫額
FROM products
GROUP BY category;
解説

NULLグループの在庫額は空になる。新商品Aは stock が NULL なので計算結果もNULLになり、合計する対象が無くなるため(SQL05・SQL06)。

SQL07-D10 category が NULL の場合に「未分類」と表示して集計するSQLを書け。分類(別名「分類」)と件数(別名「件数」)を取り出すこと。 コーディング★★☆無料
期待される結果

3行。PC 2 / 周辺機器 4 / 未分類 1

ヒント

COALESCE で置き換えた結果に別名を付け、その別名で GROUP BY する。

模範解答
SELECT COALESCE(category, '未分類') AS 分類, COUNT(*) AS 件数
FROM products
GROUP BY 分類;
解説

置き換えてからグループ化できる。レポートに「NULL」と表示するより「未分類」のほうが、読む人に意味が伝わる。

4実践シナリオ(5問)

SQL07-S01 【カテゴリ別売上一覧】分類(NULLは「未分類」、別名「分類」)ごとに、商品数(別名「商品数」)と価格合計(別名「合計」)を取り出し、合計の大きい順に並べるSQLを書け。 コーディング★★☆無料
期待される結果

3行。PC 2 173000 が先頭

ヒント

COALESCE で置き換え、GROUP BY し、ORDER BY 合計 DESC で並べる。

模範解答
SELECT
  COALESCE(category, '未分類') AS 分類,
  COUNT(*) AS 商品数,
  SUM(price) AS 合計
FROM products
GROUP BY 分類
ORDER BY 合計 DESC;
解説

実務のレポートで最も多い形。「切り口ごとに集計して、大きい順に並べる」——売上でも件数でもこの構造は変わらない。

SQL07-S02 【価格が入力済みの商品だけで集計】price が入力されている商品に限り、category ごとの件数(別名「件数」)と平均単価(別名「平均」、小数第1位まで)を取り出すSQLを書け。 コーディング★★☆無料
期待される結果

3行。周辺機器は 3 / 4400.0

ヒント

WHERE price IS NOT NULL で絞ってから GROUP BY する。ROUND で桁を揃える。

模範解答
SELECT
  category,
  COUNT(*) AS 件数,
  ROUND(AVG(price), 1) AS 平均
FROM products
WHERE price IS NOT NULL
GROUP BY category;
解説

WHERE で絞ると件数そのものが変わる。周辺機器は本来4件だが、価格未入力の1件を除いたので3件になっている。何を分母にするかを明示できるのがこの書き方の利点。

SQL07-S03 【入力状況の確認】category ごとに、全行数(別名「全件」)と price が入力済みの件数(別名「価格あり」)を並べて取り出すSQLを書け。 コーディング★★☆無料
期待される結果

3列3行。周辺機器は 4 / 3

ヒント

COUNT(*) と COUNT(price) を並べる。両者の差が欠損件数になる。

模範解答
SELECT
  category,
  COUNT(*) AS 全件,
  COUNT(price) AS 価格あり
FROM products
GROUP BY category;
解説

2つの数を並べることで欠損が見える。周辺機器だけ 4 と 3 で差があり、そこに未入力が1件あると一目で分かる(SQL06の設計思想)。

SQL07-S04 【カテゴリ別の価格帯】category ごとに、最安値(別名「最安」)・最高値(別名「最高」)・平均(別名「平均」、小数第1位まで)を取り出し、平均の高い順に並べるSQLを書け。 コーディング★★★無料
期待される結果

4列3行。PC が先頭(平均86500.0)

ヒント

MIN・MAX・ROUND(AVG(...),1) を並べ、ORDER BY 平均 DESC。

模範解答
SELECT
  category,
  MIN(price) AS 最安,
  MAX(price) AS 最高,
  ROUND(AVG(price), 1) AS 平均
FROM products
GROUP BY category
ORDER BY 平均 DESC;
解説

グループごとの価格帯が一目で分かる。PCは45000〜128000円、周辺機器は1500〜8500円と、扱う価格帯がまったく違うことが数字で示せる。

SQL07-S05 【在庫金額のカテゴリ別集計】category ごとに、商品数(別名「商品数」)と在庫金額の合計(NULLは0として扱う、別名「在庫額」)を取り出し、在庫額の大きい順に並べるSQLを書け。 コーディング★★★無料
期待される結果

3行。PC 2 955000 が先頭、NULLグループは 1 0

ヒント

COALESCE を price と stock の両方に適用してから掛ける(SQL05のS04)。

模範解答
SELECT
  category,
  COUNT(*) AS 商品数,
  SUM(COALESCE(price, 0) * COALESCE(stock, 0)) AS 在庫額
FROM products
GROUP BY category
ORDER BY 在庫額 DESC;
解説

COALESCE を入れないとNULLグループの在庫額が空欄になる(D09の結果)。0として扱えば「在庫金額ゼロ」という意味のある数字になり、並べ替えにも正しく参加する。

5仕上げ課題

SQL07-FINAL 【カテゴリ別 商品分析レポート】商品企画の担当者から「カテゴリごとの状況を1枚にまとめてほしい」と依頼された。

表示する列
① 分類(category、NULLは「未分類」)→ 別名「分類」
② 商品数 → 別名「商品数」
③ 価格が入力済みの件数 → 別名「価格登録済」
④ 平均単価(NULLを除く、小数第1位まで)→ 別名「平均単価」
⑤ 最高値 → 別名「最高値」
⑥ 在庫金額の合計(NULLは0として扱う)→ 別名「在庫額」

並び順:在庫額の大きい順

期待される結果(6列 × 3行)
分類 | 商品数 | 価格登録済 | 平均単価 | 最高値 | 在庫額
PC | 2 | 2 | 86500.0 | 128000 | 955000
周辺機器 | 4 | 3 | 4400.0 | 8500 | 467400
未分類 | 1 | 1 | 25000.0 | 25000 | 0
期待される結果

6列 × 3行が完全一致

ヒント

COALESCE で分類名を作り、その別名で GROUP BY する。COUNT(*) と COUNT(price) を使い分け、在庫額は SUM(COALESCE(price,0) * COALESCE(stock,0))。並べ替えは ORDER BY 在庫額 DESC。

模範解答
SELECT
  COALESCE(category, '未分類') AS 分類,
  COUNT(*) AS 商品数,
  COUNT(price) AS 価格登録済,
  ROUND(AVG(price), 1) AS 平均単価,
  MAX(price) AS 最高値,
  SUM(COALESCE(price, 0) * COALESCE(stock, 0)) AS 在庫額
FROM products
GROUP BY 分類
ORDER BY 在庫額 DESC;
解説

Excelのピボットテーブルと同じものが、SQL 1本で作れた。しかもこれは商品が100万件でも同じSQLで動く。

このレポートは読むだけで課題が見えるように設計されている。周辺機器は商品数4に対して価格登録済3——1件が未入力だ。未分類の在庫額が0なのは、在庫数そのものが未登録だから。数字を並べることで、データの不備が浮かび上がる

そしてPCは商品数2なのに在庫額95万円で首位に立つ。商品数4の周辺機器(46万円)の2倍以上だ。「どのカテゴリに力を入れるか」を考えるとき、商品数だけ見ていては判断を誤る。切り口を変えると別の事実が見える——これがGROUP BYで分析する意味になる。

ただし今の状態には制限がある。すべてのグループが表示されてしまうことだ。「商品数が3件以上のカテゴリだけ見たい」と思っても、WHERE では書けない(WHEREは集計前に効くため)。次章の HAVING で、この問題を解決する。