何千行ものデータを扱ってるとき、どうやって意味を見つける?そこで登場するのがGROUP BY演算子だよ。SQLのオーケストラで指揮者みたいな存在。バラバラなデータをキレイなメロディーに変えてくれる:数えたり、グループ化したり、まとめたり。例えば、各クライアントが何件注文したか、各コースに何人学生がいるか、部署ごとの給料の分布とか知りたい?全部GROUP BYの仕事!今日はこいつと仲良くなって、テーブルから最大限にデータを引き出す方法を見ていこう。
グループ化っていうのは、1つまたは複数のカラムで同じ値を持つ行を論理的なグループにまとめるプロセスだよ。これで、各グループごとに集約関数を使えるようになる。
例えば、社員テーブルがあって、各部署ごとの平均給料を知りたいとする。一つの部署が一つのグループ。SQLはGROUP BYを使って社員テーブルを部署ごとに分けて、各グループにAVG()を適用するんだ。
GROUP BYの構文
SQLでグループ化を使うときの一番大事なルール:GROUP BYを使うなら、集約関数じゃないカラムは全部GROUP BYに書かないとダメ。
構文:
SELECT カラム1,
集約関数(カラム2)
FROM テーブル
GROUP BY カラム1;
やることのステップ:
- グループ化したいカラムを
GROUP BYに指定する。 - グループごとに値を計算するために集約関数を使う。
SELECTの中で集約関数じゃないカラムは全部GROUP BYに書く。SQLはこの辺厳しいから、忘れるとエラーで怒られるよ。
例:学生を学部ごとにグループ化
例えば、studentsテーブルがあって、学生のデータが入ってるとする:
| id | name | faculty | gpa |
|---|---|---|---|
| 1 | Alex Lin | ComputerSci | 3.8 |
| 2 | Maria Chi | Math | 3.5 |
| 3 | Anna Song | ComputerSci | 4.0 |
| 4 | Otto Art | Math | 3.9 |
| 5 | Liam Park | Physics | 3.7 |
今度は、各学部ごとの平均GPAを知りたい。だからGROUP BYを使ったクエリを書くよ:
SELECT faculty, AVG(gpa) AS avg_gpa
FROM students
GROUP BY faculty;
結果:
| faculty | avg_gpa |
|---|---|
| ComputerSci | 3.9 |
| Math | 3.7 |
| Physics | 3.7 |
SQLはまずfacultyカラムの値ごとにデータをグループ分けして、それぞれのグループにAVG()関数を適用したんだ。
GROUP BYの使い方のポイント
SELECT内のカラムの要件
SQLは、SELECTで指定したカラムのうち、SUM()やCOUNT()みたいな集約関数を使ってないカラムは全部GROUP BYに書くように要求してくる。グループ化しないと、どの値を出せばいいかSQLが分からないからだよ。
例えば、次のクエリを実行するとエラーになる:
SELECT name, AVG(gpa)
FROM students
GROUP BY faculty;
エラー:nameカラムがGROUP BYに指定されてない。これを直すには、nameもGROUP BYに追加する:
SELECT name, AVG(gpa)
FROM students
GROUP BY faculty, name;
でもこれだと、今度は個々の学生ごとにグループ化されちゃうから、最初に欲しかった結果とは違うよね。
- 複数カラムでのグループ化
1つだけじゃなくて、複数のカラムでグループ化もできる。例えば、学部だけじゃなくて名前でもグループ化したいなら、GROUP BYに2つ目のカラムを追加するだけ:
SELECT faculty, name, AVG(gpa) AS avg_gpa
FROM students
GROUP BY faculty, name;
元のテーブル:
| id | name | faculty | gpa |
|---|---|---|---|
| 1 | Alex Lin | ComputerSci | 3.8 |
| 2 | Maria Chi | Math | 3.5 |
| 3 | Anna Song | ComputerSci | 4.0 |
| 4 | Otto Art | Math | 3.9 |
| 5 | Liam Park | Physics | 3.7 |
結果:
| faculty | name | avg_gpa |
|---|---|---|
| ComputerSci | Alex Lin | 3.8 |
| ComputerSci | Anna Song | 4.0 |
| Math | Maria Chi | 3.5 |
| Math | Otto Art | 3.9 |
| Physics | Liam Park | 3.7 |
- 複数の集約関数を使ったグループ化
1つの関数だけにしないでOK!例えば、学部ごとの学生数と平均GPAを両方出したい場合:
SELECT faculty,
COUNT(*) AS student_count,
AVG(gpa) AS avg_gpa
FROM students
GROUP BY faculty;
元のテーブル:
| id | name | faculty | gpa |
|---|---|---|---|
| 1 | Alex Lin | ComputerSci | 3.8 |
| 2 | Maria Chi | Math | 3.5 |
| 3 | Anna Song | ComputerSci | 4.0 |
| 4 | Otto Art | Math | 3.9 |
| 5 | Liam Park | Physics | 3.7 |
結果:
| faculty | student_count | avg_gpa |
|---|---|---|
| ComputerSci | 2 | 3.9 |
| Math | 2 | 3.7 |
| Physics | 1 | 3.7 |
SQLでのグループ化の特徴:選べるもの・選べないもの
グループ化クエリは書くの簡単だけど、たぶん半分くらいは最初うまく動かない。グループ化は、普段自分の頭でやってるのとちょっと違う感じで動くんだ。
SQLクエリにGROUP BYがあるなら、結果のカラムは全部「計算された式」だと思って。SELECTのカラムは2種類だけ:
- グループのカラムを使って集約関数で計算されるやつ。
- GROUP BYで指定したカラム(これでグループ化されてるやつ)。
もしstudentsテーブルでGROUP BYクエリをやるなら、結果テーブルに個々の学生は出せない!出せるのは平均身長、平均体重、平均GPAみたいなやつ。こういうコードは動かない:
SELECT faculty, name
FROM students
GROUP BY faculty;
なんでか、ちょっと考えてみよう。
GROUP BY faculty演算子は、studentsテーブルの学生をfacultyが同じグループに分ける。グループ内の全員が同じfacultyだから、グループにはfaculty属性があるって言える。でもnameはみんな違う。だからグループにはname属性がないんだ。
GROUP BY faculty, gender演算子は、studentsテーブルの学生をfacultyとgenderが同じグループに分ける。だからグループ内の全員が同じfacultyとgenderを持つ。つまりグループにはfacultyとgender属性がある。でもグループに共通のnameはやっぱりない。
こう書くのはOK:
SELECT faculty, gender
FROM students
GROUP BY faculty, gender;
こんな感じでもOK:
SELECT
faculty,
gender,
AVG(age) as group_avg_age, -- groupのage値から計算される値
MAX(high) as group_high -- groupのhigh値から計算される値
FROM students
GROUP BY faculty, gender;
でも、単にageやhighをSELECTで使うのはこの場合ダメ。
GROUP BYでよくあるミス
GROUP BYでクエリを書き始めたら、こんな落とし穴に注意してね:
SELECTに全部のカラムを書いてない。 集約じゃないカラムは全部GROUP BYに書くのを忘れずに。そうしないとSQLはどう表示していいか分からない。NULLでグループ化。NULL値は別グループとして扱われる。カラムにNULLがあると、SQLはNULL用のグループを作るよ。グループが細かすぎる。
GROUP BYにカラムを入れすぎると、細かすぎる結果になって分析しにくくなることも。
これでGROUP BYを使ってデータを効率よくグループ化できるようになったね。SQLで一番強力なツールの一つで、集約データを簡単に扱ったり、構造化されたレポートを作れるよ。次はHAVINGを使った追加フィルターのやり方も見ていこう!
GO TO FULL VERSION