資格道場 › レッスン一覧 › データベース
データベース・7/13・12分

SQLの集計(集計関数・GROUP BY・HAVING)

ゴール

集計関数(COUNT・SUM・AVG・MAX・MIN)の値を表から計算でき、GROUP BY・HAVING・ORDER BY の穴埋め問題で正しい組み合わせを選べるようになる。

「部署ごとの売上合計」「月ごとの件数」のような集計は、管理画面やレポートの裏で毎日動いている SQL です。科目Aでは、GROUP BY と HAVING の穴埋め問題と、表を見て集計値を計算する問題がくり返し出ています。

このレッスンで稽古する(用語チェック・過去問)

結論:集計は「グループに分ける→1グループを1行にまとめる」

集計関数は、たくさんの行を1つの値にまとめる関数です。GROUP BY は、まとめる前に行をグループ分けする句です。HAVING は、まとめた後のグループを条件で絞り込む句です。

たとえば、レシートの山を「店ごと」に箱へ仕分けて(GROUP BY)、箱ごとに合計金額を出し(SUM)、合計1万円以上の箱だけ残す(HAVING)、という流れです。

レシートの山(1枚=1行)店A6000円店B3000円店A5000円店B4000円店C12000円GROUP BY 店(店ごとの箱へ)店Aの箱6000円・5000円店Bの箱3000円・4000円店Cの箱12000円SUM(金額)(箱ごとに合計)11000円7000円12000円HAVING SUM(金額) >= 10000店A 残る店B 消える店C 残るHAVING は箱(グループ)ごとに判定する
レシートを店ごとの箱に分け(GROUP BY)、箱ごとに合計し(SUM)、合計1万円以上の箱だけ残す(HAVING)流れ。

集計関数5つ:何を数えるか

関数 返す値 出庫記録の例(数量 3,2,1,2)
COUNT(*) 行の数 4
COUNT(列) その列が NULL(値なし)でない行の数 4
SUM(列) 合計 3+2+1+2 = 8
AVG(列) 平均(合計÷件数) 8÷4 = 2
MAX(列) / MIN(列) 最大 / 最小 3 / 1

COUNT は中身の数値を見ずに行を数えます。 数量が3でも100でも、1行は1と数えます。WHERE があれば、条件に合う行だけを集計します。

SQLが処理される順番

書く順番と処理される順番は違います。この順番が分かると、「WHERE に集計関数を書けない」理由が分かります。

flowchart LR
  A["FROM 表を読む"] --> B["WHERE 行を絞る"]
  B --> C["GROUP BY グループ分け"]
  C --> D["HAVING グループを絞る"]
  D --> E["SELECT 列と集計値を出す"]
  E --> F["ORDER BY 並べ替え"]

WHERE が動く時点では、まだグループ分けも集計もしていません。そのため、WHERE AVG(点数) >= 80 は書けません。集計した結果で絞るときは、必ず HAVING を使います。

WHERE と HAVING の違い(最頻出)

WHERE HAVING
絞る対象 1行ずつ グループ(集計後)
実行タイミング GROUP BY の前 GROUP BY の後
集計関数を書けるか 書けない 書ける
例 WHERE 点数 >= 80(80点以上の答案だけ) HAVING AVG(点数) >= 80(平均80点以上の学生だけ)

「平均が80点以上の学生」は、平均を出してからでないと判定できないので HAVING です。WHERE 点数 >= 80 と書くと「80点以上の科目だけで平均を出す」という別の処理になり、80点未満の科目がある学生まで残ってしまいます。

元の答案(2人×2科目=4行)学生A:90点・70点学生B:84点・60点WHERE 点数 >= 80HAVING AVG(点数) >= 80① 先に1行ずつ絞る70点・60点の行が消える① 先に学生ごとに平均A:80点 B:72点② 残った点数で平均A:90点 B:84点② 平均で絞る80点以上はAだけA・Bとも残る(誤り)Aだけ残る(正しい)
同じ答案に WHERE 点数 >= 80 と HAVING AVG(点数) >= 80 を使うと、絞る順番が違うので結果が変わる。

GROUP BY のルール:SELECT の列と一致させる

SELECT に書く列は、集計関数の中に入れるか、GROUP BY に書くかのどちらかにします。グループを1行にまとめるので、グループ内で値がそろわない列は表示できないからです。

  • ○ SELECT 注文日, AVG(数量) … GROUP BY 注文日
  • × SELECT 注文日, AVG(数量) … GROUP BY 数量(注文日が1つの値に決まらない)
  • × AVG(SUM(数量))(集計関数の入れ子は不可)
  • × WHERE SUM(数量) > 1000(WHERE に集計関数は書けない)

GROUP BY に複数の列を書くと、その組み合わせごとに1グループになります。「クラスごと、教科ごと」なら GROUP BY クラス名, 教科名 です。ここに学生番号を足すと、1人ずつのグループになり、平均を出す意味がなくなります。

元の行(6行)グループ(4つ)1組・数学・70点1組・数学・90点1組・英語・60点2組・数学・80点2組・英語・50点2組・英語・70点1組・数学平均 80点1組・英語 平均 60点2組・数学 平均 80点2組・英語平均 60点GROUP BY クラス名, 教科名 → 4グループ学生番号も足すと 6グループ(1人ずつになり、平均を出す意味がない)
GROUP BY クラス名, 教科名 では、クラスと教科の組み合わせごとに1グループになり、6行が4グループにまとまる。

ORDER BY:最後に並べ替える

ORDER BY は結果の並び順を指定します。書かなければ昇順(ASC:小さい順)で、降順にしたいときは DESC を付けます。「クラス名、教科名の昇順」と言われたら ORDER BY クラス名, 教科名 です。

ORDER BY は SELECT の後に動くので、AS 平均点 で付けた別名も使えます。ただし試験では「問題文に書かれた並び順と同じ列を指定しているか」だけを確認すれば十分です。

試験での問われ方

  • 集計値の比較:表と4つの SQL が示され、「最も大きな値はどれか」と問われる。COUNT(*) の行数を数量の値と取り違えさせる問題
  • GROUP BY・HAVING の穴埋め:「学生ごと」「クラスごと・教科ごと」という問題文から、グループ化する列と、条件を HAVING に書くか WHERE に書くかを選ばせる
  • GROUP BY・ORDER BY の穴埋め:グループに余計な列(学生番号・平均点)を足した選択肢や、並び順の列が問題文と違う選択肢でひっかける
  • グループの数:表を示し、複数の列でグループ化したときのグループ数を数えさせる
  • 構文の正誤:SELECT の列と GROUP BY の列が合っていない、集計関数を入れ子にしている、WHERE に集計関数を書いている、のどれかが誤りの選択肢になる
例題 1

4つの集計値を計算して比べる

“出庫記録” 表に対する SQL 文のうち,最も大きな値が得られるものはどれか。
出庫記録
商品番号日付数量
NP2002015-10-103
FP2332015-10-102
NP2002015-10-111
FP2332015-10-112

ア SELECT AVG(数量) FROM 出庫記録 WHERE 商品番号 = 'NP200'
イ SELECT COUNT(*) FROM 出庫記録
ウ SELECT MAX(数量) FROM 出庫記録
エ SELECT SUM(数量) FROM 出庫記録 WHERE 日付 = '2015-10-11'

出典:平成27年度 秋期 基本情報技術者試験 午前 問28

解き方と答えを見る
  1. 表を確認する:NP200/10-10/3、FP233/10-10/2、NP200/10-11/1、FP233/10-11/2 の4行
  2. ア AVG(数量) WHERE 商品番号='NP200':対象は3と1 → (3+1)÷2 = 2
  3. イ COUNT(*):行の数を数える → 4行なので 4
  4. ウ MAX(数量):3,2,1,2 の最大 → 3
  5. エ SUM(数量) WHERE 日付='2015-10-11':対象は1と2 → 1+2 = 3
  6. 比べる:ア2、イ4、ウ3、エ3 → 最大はイ

答え:イ(COUNT(*) = 4)。数量の最大値3や合計3と、行数4を混同しないこと。

例題 2

2つの列でグループ化したときのグループ数

“伝票”表のデータを“伝票コード”ごと“税区分”ごとにグループ化したときに得られるグループの数はどれか。
伝票
伝票コード商品コード販売価格数量税区分
D000S0001,0001Z000
D000S0011,1002Z000
D000S0021,2003Z008
D001S0031,2001Z008
D001S0041,1002Z010
D001S0051,0003Z010

ア 3
イ 4
ウ 5
エ 6

出典:基本情報技術者試験 科目A免除 修了試験 20260726 実施分 問21

解き方と答えを見る
  1. グループ化する列は伝票コードと税区分の2つ → 2つの値の組み合わせが同じ行を1グループにする
  2. 1行ずつ組み合わせを書き出す:(D000,Z000)(D000,Z000)(D000,Z008)(D001,Z008)(D001,Z010)(D001,Z010)
  3. 重複を除く:(D000,Z000)、(D000,Z008)、(D001,Z008)、(D001,Z010)
  4. 数える → 4グループ

答え:イ(4)。伝票コードだけなら2、税区分だけなら3になるが、それはひっかけ。

例題 3

「学生ごと・平均80点以上」の穴埋め

"得点"表から,学生ごとに全科目の点数の平均を算出し,平均が 80 点以上の学生の学生番号とその平均点を求める。a に入れる適切な字句はどれか。ここで,実線の下線は主キーを表す。
得点(学生番号, 科目, 点数) ※学生番号と科目に実線の下線(主キー) 〔SQL 文〕 SELECT 学生番号, AVG(点数) FROM 得点 GROUP BY [a]

ア 科目 HAVING AVG(点数) >= 80
イ 科目 WHERE 点数 >= 80
ウ 学生番号 HAVING AVG(点数) >= 80
エ 学生番号 WHERE 点数 >= 80

出典:令和元年度 秋期 基本情報技術者試験 午前 問26

解き方と答えを見る
  1. 「学生ごとに平均」→ グループは学生 → GROUP BY 学生番号(アとイは科目なので除外)
  2. 「平均が80点以上」→ 集計した後の条件 → HAVING AVG(点数) >= 80
  3. WHERE 点数 >= 80 は1行ずつの条件なので、「平均80点以上」という意味にならない(エは除外)

答え:ウ(学生番号 HAVING AVG(点数) >= 80)

要点(試験直前に読む)

  • COUNT(*) は行数。中身の数値は見ない。表の問題では、まず行を数える
  • 処理順は FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY
  • 集計結果の条件(AVG(点数) >= 80 など)は HAVING に書く。WHERE には集計関数を書けない
  • SELECT の列は、集計関数の中に入れるか GROUP BY に書くかのどちらか
  • 「AごとBごと」なら GROUP BY A, B。グループ数は (A,B) の組み合わせが何種類あるかで数える
  • 並び順の指定は ORDER BY で、既定は昇順

ひっかけ

  • COUNT(*) の行数を、数量の合計や最大値と取り違える → COUNT は1行を1と数えるだけ
  • 集計後の条件を WHERE に書いた選択肢 → 集計関数が条件に入っていれば HAVING
  • GROUP BY に学生番号や平均点などの余計な列が入っている → 問題文の「〜ごと」に出てくる列だけを書く
  • 2つの列でグループ化したのに、片方の列の種類数だけを数える → 組み合わせを書き出して重複を除く
  • 集計関数の入れ子(AVG(SUM(…)))や、SELECT と GROUP BY の列が合わない構文 → どちらも誤り

用語(14)

集計関数 しゅうけいかんすう
複数の行の値をまとめて1つの値(件数・合計・平均・最大・最小)にする関数。
COUNT(*) カウント
条件に合う行の数を返す関数で、列の値の大きさには関係しない。
⚠ SUM(値の合計)と混同しやすい。COUNT は行数、SUM は中身の数値の合計。
COUNT(列名) カウント
指定した列が NULL でない行だけを数える関数。
⚠ COUNT(*) は NULL の行も含めて全部の行を数える。
SUM サム
指定した列の値の合計を返す関数。
⚠ COUNT と区別する。数量3の行は SUM では3、COUNT では1と数える。
AVG アベレージ(average)
指定した列の値の平均(合計÷件数)を返す関数。
MAX / MIN マックス / ミン(maximum / minimum)
指定した列の最大値/最小値を返す関数。
NULL ヌル
値が入っていないことを表す特別な状態で、0や空文字とは別のもの。
⚠ 0 とは違う。COUNT(列) や AVG は NULL の行を計算に入れない。
GROUP BY 句 グループバイ
指定した列の値(複数指定ならその組み合わせ)が同じ行を1つのグループにまとめる句。
⚠ ORDER BY は並べ替えるだけで、行をまとめない。
HAVING 句 ハビング
GROUP BY でまとめた後のグループを、集計値などの条件で絞り込む句。
⚠ WHERE は集計前に1行ずつ絞る。集計関数を条件に使うなら HAVING。
WHERE 句 ホエア
グループ化の前に、表の行を1行ずつ条件で絞り込む句。
⚠ HAVING と違い、集計関数を書けない。
ORDER BY 句 オーダーバイ
結果の行を指定した列の順に並べ替える句で、既定は昇順。
⚠ GROUP BY と違い、グループ化はしない。
ASC / DESC アセンディング / ディセンディング(ascending / descending)
ORDER BY で指定する並び順で、ASC は昇順(小さい順)、DESC は降順(大きい順)。
⚠ 何も書かなければ ASC になる。
AS(別名) アズ
SELECT で出す列や集計値に、表示用の名前を付ける指定。
グループ化列 グループかれつ
GROUP BY に書いた列のことで、SELECT ではこの列か集計関数の結果だけを表示できる。
⚠ グループ化列に学生番号などを足すと、グループが細かくなりすぎて集計の意味が変わる。

このレッスンの過去問

このレッスンで稽古する