SQL:集計関数(COUNT / SUM / AVG)
1学習の目的
- COUNT・SUM・AVG・MIN・MAX で、たくさんの行を1つの数値にまとめられるようになる。「何件あるか」「合計いくらか」が1行で分かるようになる。
- 集計関数がNULLをどう扱うかを理解する。とくに平均値は分母が変わるため、NULLの扱いを誤ると数字そのものが間違う。
2基礎解説
ここまでは行を取り出すだけでした。集計関数を使うと、複数の行を1つの値にまとめられます。
| 関数 | 意味 | NULLの扱い |
|---|---|---|
| COUNT(*) | 行数 | NULLの行も数える |
| COUNT(列) | その列の入力済み件数 | NULLは除く |
| SUM(列) / AVG(列) | 合計 / 平均 | NULLは無視される |
| MIN(列) / MAX(列) | 最小 / 最大 | NULLは無視される |
- 集計関数は結果が1行になる。5行でも1万行でも、返ってくるのは1行だけ。
- AVG の分母はNULLを除いた件数。7行のうち1行がNULLなら、6で割られる。「NULLを0とみなして7で割る」のとは答えが変わる——どちらが正しいかは業務の定義次第。
- COUNT(*) と COUNT(列名) は別物(SQL05)。全行を数えたいのか、入力済みを数えたいのかで使い分ける。
- COUNT(DISTINCT 列) で重複を除いた種類数が数えられる。「何種類のカテゴリがあるか」を1行で出せる。
- 集計関数の中には計算式も書ける。SUM(price * stock) で在庫総額が一発で出る。
現場使用例:今月の売上合計、会員数、平均単価、最高購入額、商品カテゴリの種類数。レポートの数字はほぼすべて集計関数から生まれる。
-- 行数を数える SELECT COUNT(*) AS 件数 FROM products; -- 7 -- 合計と平均 SELECT SUM(price) AS 合計, -- 211200 AVG(price) AS 平均 -- 35200.0 FROM products; -- ⚠ AVG の分母はNULLを除いた6件 SELECT SUM(price) AS 合計, -- 211200 COUNT(price) AS 分母, -- 6(7ではない) AVG(price) AS 平均, -- 35200.0 AVG(COALESCE(price,0)) AS 平均0埋め -- 30171.4…(分母7) FROM products; -- 最小・最大 SELECT MIN(price) AS 最安, MAX(price) AS 最高 FROM products; -- 絞り込んでから集計する SELECT COUNT(*) AS 件数, SUM(price) AS 合計 FROM products WHERE category = '周辺機器'; -- 計算式もまとめて集計できる SELECT SUM(price * stock) AS 在庫総額 FROM products;
3基本ドリル(10問)
1行。件数 7
COUNT(*) を使う。AS で別名を付ける。
SELECT COUNT(*) AS 件数 FROM products;
集計関数は結果が1行になる。7行のテーブルから返るのは「7」という1つの値だけ。
A. 元の行数と同じ B. 1行 C. 0行 D. 条件による 選択★☆☆無料
解答 == B
「まとめる」という言葉の意味。
B
複数の行を1つの値に集約するのが集計関数。次章の GROUP BY を使うと、グループごとに1行ずつ返るようになる。
1行。合計 211200 / 平均 35200.0
SUM(price) と AVG(price) をカンマで並べる。
SELECT SUM(price) AS 合計, AVG(price) AS 平均 FROM products;
複数の集計を1回のSQLで取れる。合計と平均を別々に問い合わせる必要はない。
SELECT ____(price) AS 最高 FROM products;
1行。最高 128000
「最大」を意味する3文字の関数。
MAX
対になるのは MIN。文字列にも使えるので、名前の五十音順で最初・最後を取ることもできる。
A. 7 B. 6 C. 1 D. 0 選択★☆☆無料
解答 == B
AVG はNULLをどう扱うか。
B
NULLは無視されるので分母は6。「未入力を0として7で割る」のとは答えが変わる。どちらが正しいかは業務の定義次第で、SQLが決めることではない。
1行。分類数 2
COUNT(DISTINCT category) と書く。
SELECT COUNT(DISTINCT category) AS 分類数 FROM products;
DISTINCT はNULLも除くので、NULLのカテゴリは種類に数えられない。結果は「PC」「周辺機器」の2種類。
SELECT ____(price * stock) AS 在庫総額 FROM products;
1行。在庫総額 1422400
「合計」を意味する3文字の関数。
SUM
集計関数の中に計算式を書ける。各行で price × stock を計算してから、それらを合計している。
A. 0 B. NULL C. エラー D. 空文字 選択★★☆無料
解答 == B
「合計すべき値が存在しない」状態。
B
0ではなくNULLが返るのが要注意。画面に表示する前に COALESCE(SUM(price), 0) で0に置き換えるのが実務の定石。
1行。件数 4 / 合計 13200
WHERE で絞ってから集計する。句の順番は FROM → WHERE。
SELECT COUNT(*) AS 件数, SUM(price) AS 合計 FROM products WHERE category = '周辺機器';
絞り込んでから集計するのが基本。件数は4件だが、そのうち1件はpriceがNULLなので合計には3件分しか入っていない。
1行。平均単価 35200.0
ROUND(値, 桁数) を使う。ROUND(AVG(price), 1) のように入れ子にできる。
SELECT ROUND(AVG(price), 1) AS 平均単価 FROM products;
AVG の結果は小数が長くなりがちなので、レポートでは ROUND で桁を揃える。関数は入れ子にできる。
4実践シナリオ(5問)
① 全商品数(別名「全商品」)② 価格が入力済みの件数(別名「価格あり」)③ カテゴリの種類数(別名「分類数」) コーディング★★☆無料
1行。7 / 6 / 2
COUNT(*)、COUNT(price)、COUNT(DISTINCT category) を並べる。
SELECT COUNT(*) AS 全商品, COUNT(price) AS 価格あり, COUNT(DISTINCT category) AS 分類数 FROM products;
3種類の COUNT を使い分けている。全体・入力済み・種類数はそれぞれ別の情報で、テーブルを初めて見るときの基本調査になる。
1行。1500 / 128000 / 35200.0
MIN・MAX・ROUND(AVG(...), 1) を並べる。
SELECT MIN(price) AS 最安, MAX(price) AS 最高, ROUND(AVG(price), 1) AS 平均 FROM products;
最小・最大・平均の3つでデータの分布が見える。平均だけでは「高い商品と安い商品が混在しているのか」が分からない。
1行。2 / 173000 / 86500.0
WHERE で絞ってから3つの集計関数を並べる。
SELECT COUNT(*) AS 件数, SUM(price) AS 合計, AVG(price) AS 平均 FROM products WHERE category = 'PC';
カテゴリを変えれば別の集計が取れるが、毎回WHEREを書き換えるのは非効率。次章の GROUP BY を使えば、全カテゴリを1回で集計できる。
① NULLを除いた平均(別名「除外平均」)② NULLを0として扱った平均(別名「ゼロ埋め平均」、小数第1位まで) コーディング★★★無料
1行。35200.0 / 30171.4
①は AVG(price)、②は AVG(COALESCE(price, 0))。②は ROUND で桁を揃える。
SELECT AVG(price) AS 除外平均, ROUND(AVG(COALESCE(price, 0)), 1) AS ゼロ埋め平均 FROM products;
同じデータから2つの異なる平均が出る。差は約5000円——分母が6か7かの違いだけでこれだけ変わる。どちらを報告するかは業務の定義で決めるべきで、SQLが自動で決めてくれるわけではない。
1行。総額 1422400 / 対象件数 5
SUM(price * stock) と COUNT(price * stock) を並べる。NULLを含む計算結果はNULLになるので、COUNT から除かれる。
SELECT SUM(price * stock) AS 総額, COUNT(price * stock) AS 対象件数 FROM products;
7件中5件しか計算できていないことが分かる。新商品Aとサンプル品はNULLを含むため計算結果もNULLになり、集計から除かれた。総額だけ見ていては、この欠損に気づけない。
5仕上げ課題
表示する列
① 全商品数(別名「商品数」)
② カテゴリの種類数(別名「分類数」)
③ 価格が入力済みの件数(別名「価格登録済」)
④ 最安値(別名「最安値」)
⑤ 最高値(別名「最高値」)
⑥ 平均単価(NULLを除く、小数第1位まで)→ 別名「平均単価」
⑦ 在庫総額(price × stock の合計)→ 別名「在庫総額」
期待される結果(7列 × 1行)
商品数 | 分類数 | 価格登録済 | 最安値 | 最高値 | 平均単価 | 在庫総額
7 | 2 | 6 | 1500 | 128000 | 35200.0 | 1422400
7列 × 1行が完全一致
COUNT(*)、COUNT(DISTINCT category)、COUNT(price)、MIN、MAX、ROUND(AVG(price),1)、SUM(price*stock) の7つを並べる。WHERE は不要。
SELECT COUNT(*) AS 商品数, COUNT(DISTINCT category) AS 分類数, COUNT(price) AS 価格登録済, MIN(price) AS 最安値, MAX(price) AS 最高値, ROUND(AVG(price), 1) AS 平均単価, SUM(price * stock) AS 在庫総額 FROM products;
7行のテーブルが、7つの数字に凝縮された。これが1万件でも100万件でも、返ってくるのは同じ1行だ。集計関数の威力はデータが大きくなるほど効いてくる。
このレポートには意図的に「商品数7」と「価格登録済6」を並べている。この2つの差が1あることで、読む人は「1件だけ価格が未入力だ」と気づける。集計値を1つだけ出すのではなく、比較できる形で並べる——これがレポート設計のコツだ。
そして平均単価35200円は6件で割った値である点に注意してほしい。もし「未入力は0円」という業務ルールなら、正しい平均は30171円になる。同じデータ、同じSQL、それでも答えが2つある。どちらが正しいかは、データベースではなく業務が決める。SQLを書く前に定義を確認する——これが数字を扱う仕事の基本になる。
次章では GROUP BY を学ぶ。今回は全体で1行だったが、「カテゴリごとに1行」という集計ができるようになり、レポートが一気に実用的になる。