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

GROUP BYでグループ化する

考えてみよう

「役割ごとの平均給料を知りたい」——前回学んだAVGだけで、これは求められるでしょうか?

前回のAVGは、テーブル全体をまとめて1つの平均を出すものでした。でも「エンジニアの平均」「パイロットの平均」のように、役割ごとに分けて集計したい場合はどうすればいいでしょう。少し考えてから読み進めてください。

グループごとに集計するGROUP BY

行をいくつかのグループに分けてから集計するには、GROUP BY句を使います。

SELECT グループ化する列, 集計関数(列名)
FROM テーブル名
GROUP BY グループ化する列;

crewテーブルで、役割(role)ごとの人数と平均給料を求めてみましょう。トウマとリョウは、前回学んだとおり配属先が決まっておらず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

SELECT role, COUNT(*) AS 人数, AVG(salary) AS 平均給料
FROM crew
GROUP BY role;

結果:

role人数平均給料
エンジニア3380000
パイロット3400000
医師2485000
研究員2325000

GROUP BY roleと書くことで、SQLはまずroleの値が同じ行同士を1つのグループにまとめます。そのうえで、グループごとにCOUNT(*)AVG(salary)を計算しているのです。

今回のcrewテーブルはsalaryにNULLの乗員がいないため、それぞれのグループに含まれる全員分の値で平均が計算されています。前回学んだ「集計関数はNULLを自動的に除外する」というルールは、GROUP BYでグループごとに計算するときも変わらず働きます。もしsalaryがNULLの乗員がいれば、その行はやはりグループの集計から自動的に除かれます。

SELECTに書ける列のルール

GROUP BYを使うとき、SELECTに書ける列には決まりがあります。書けるのは、次の2種類だけです。

  1. GROUP BYに指定した列
  2. 集計関数(COUNT・SUM・AVGなど)

たとえば、次のようなSQLはエラーになるか、意図しない結果になります。

-- × 危険:nameはGROUP BYにも集計関数にも入っていない
SELECT name, role, AVG(salary)
FROM crew
GROUP BY role;

roleでグループ化すると、「エンジニア」グループにはユミ・ソラ・アカリの3人が含まれます。ここでnameをそのまま選ぼうとしても、SQLは「3人のうち、どの名前を表示すればいいのか」を決められません。1つのグループに複数の行がある以上、集計されていない列をそのまま出すことはできないのです。

正しくは、こう書きます。

-- ○ 正しい:GROUP BYの列と集計関数だけ
SELECT role, AVG(salary) AS 平均給料
FROM crew
GROUP BY role;

「GROUP BYした列」か「集計した値」しかSELECTに書けない——これはGROUP BYを使ううえで、最初につまずきやすいルールです。ここでしっかり押さえておきましょう。

確認問題

GROUP BY roleを使ったとき、SELECTにそのまま書ける列として正しいものはどれでしょう? nameroleのどちらでしょうか?

答えを見る

正解はroleです。roleGROUP BYに指定した列なのでそのまま書けますが、nameはグループ化されていない列なので、集計関数を使わずにそのまま書くことはできません。

GROUP BYを使えば、「役割ごと」「出身地ごと」など、好きな切り口でデータをまとめて眺められるようになります。次回は、そのグループ自体をさらに条件で絞り込むHAVINGを学びます。

/sql/game/ の SQL DIVER で、GROUP BYによるグループ集計を実際にタイプして確かめてみましょう。

ゆみちゃん
ゆみ

GROUP BYは「グループごとの集計係」。SELECTに書けるのは「GROUP BYした列」と「集計関数」だけ——これさえ覚えれば、もう迷わないよ!