STAGE 04 — 4-1 / 集計とグループ化

集計関数

考えてみよう

「乗員は全部で何人?」「給料の平均はいくら?」——こういう質問に、これまで学んだSELECTやWHEREだけで答えられるでしょうか?

WHEREやORDER BYは「行そのもの」を扱う道具でした。でも「件数」や「合計」「平均」は、行を1つずつ見るのではなく、全体をまとめた1つの数字です。少し考えてから読み進めてください。

集計関数とは

複数の行をまとめて1つの値にする関数を、集計関数と呼びます。代表的なものは次の5つです。

crewテーブルで確認しましょう。アマテラスの乗員は全部で10人ですが、そのうちトウマとリョウは、まだ配属先が決まっておらずhomeが未設定(NULL)です。

crew テーブル:

idnameroleagehomesalary
1ユミエンジニア24地球320000
2ケンジパイロット35火星400000
3サクラ医師41地球520000
4ハルト医師33450000
5レイ研究員29火星380000
6ミドリパイロット27地球360000
7ソラエンジニア45470000
8トウマ研究員24NULL270000
9アカリエンジニア31火星350000
10リョウパイロット38NULL440000

COUNT(*)とCOUNT(列名)の違い

まずは件数です。全乗員数を数えてみましょう。

SELECT COUNT(*) FROM crew;

結果:

COUNT(*)
10

COUNT(*)は、値がNULLかどうかに関係なく、行そのものの数を数えます。一方、COUNT(列名)とすると、その列がNULLではない行だけを数えます。

SELECT COUNT(home) FROM crew;

結果:

COUNT(home)
8

トウマとリョウのhomeはNULLなので、COUNT(home)では数えられず、結果は8になります。COUNT(*)は行数、COUNT(列名)はその列にNULLではない値がある行数——この違いを押さえておきましょう。

SUM・AVG・MAX・MIN とNULLの扱い

給料に関する集計をまとめて求めてみましょう。

SELECT
  SUM(salary) AS 給料合計,
  AVG(salary) AS 給料平均,
  MAX(salary) AS 最高給料,
  MIN(salary) AS 最低給料
FROM crew;

結果:

給料合計給料平均最高給料最低給料
3960000396000520000270000

今回のcrewテーブルはsalaryにNULLの乗員がいないため、10人全員の値を使ってSUMAVGMAXMINが計算されています。とはいえ集計関数には、NULLは集計の対象から自動的に除外されるという重要な性質があります。前のセクションで見たCOUNT(home)のように、もしNULLの行があれば、その行はSUMにもAVGにも一切含まれません。

もしNULLを「0円」として計算してしまったら、平均はまったく違う数字になってしまいます。NULLは0ではなく「値が分からない」という状態なので、集計関数はそれを賢く無視してくれるのです。

MAXMINも同様に、NULLの行があれば比較の対象から外れます。文字列の列にもMAXMINは使え、その場合は文字コード順で最大・最小が決まります。

確認問題

crewテーブルに10人の乗員がいて、うちトウマとリョウの2人だけhomeがNULLです。SELECT COUNT(*), COUNT(home) FROM crew;を実行すると、結果はどうなるでしょう?

答えを見る

正解はCOUNT(*)が10、COUNT(home)が8です。COUNT(*)は行数そのものを数えるのでNULLの行も含みますが、COUNT(列名)はその列がNULLではない行だけを数えます。

集計関数を使うと、データベースに「全体としてどうなっているか」を1つの数字で答えてもらえるようになります。次回は、この集計を「グループごと」に行うGROUP BYを学びます。

/sql/game/ の SQL DIVER で、COUNT・SUM・AVG・MAX・MINを実際にタイプして試してみましょう。

ゆみちゃん
ゆみ

集計関数のポイントはただ1つ、NULLは無視されるということ。COUNT(*)とCOUNT(列名)の違いさえ押さえれば、あとは名前の通りの働きをしてくれるよ!