さて、そろそろ人生について語る時間だね。避けられない側面、それはミス。ミスを見つけて直して理解するのは、データを扱う上で絶対に必要なこと。じゃあ、SQLのJOINでどんな落とし穴があるのか、一緒に見ていこう!
ミス1: 結合条件を忘れる — デカルト積の発生
一番ありがちなミスは、ONで結合条件を書くのを忘れること。これやるとデカルト積が発生して、1つ目のテーブルの各行が2つ目のテーブルの全行と結合されちゃう。結果、意味不明な大量の行ができて、混乱するだけ。
例を見てみよう。たとえば、こんなテーブルがあるとする:
学生 (students):
| student_id | name |
|---|---|
| 1 | Otto |
| 2 | Anna |
コース (courses):
| course_id | course_name |
|---|---|
| 101 | 数学 |
| 102 | 歴史 |
じゃあ、ONを忘れてクエリを書いてみる:
SELECT *
FROM students
JOIN courses;
結果:
| student_id | name | course_id | course_name |
|---|---|---|---|
| 1 | Otto | 101 | 数学 |
| 1 | Otto | 102 | 歴史 |
| 2 | Anna | 101 | 数学 |
| 2 | Anna | 102 | 歴史 |
これ、正しくないよね?この悪夢がデカルト積ってやつ。
どう直すか: ONを使って、テーブル同士のつながりをちゃんと指定しよう。
SELECT *
FROM students
JOIN courses
ON students.student_id = courses.course_id;
そして、ここでまた新たなミスの章が始まる…
おバカ防止機能
この問題はあまりにもよくあるので、PostgreSQLではONや条件なしのJOINは禁止されてるよ。
もし本当に全行を全部結合したいなら、JOINを使わずにこう書ける:
SELECT *
FROM students, courses;
もう1つ、JOINがONなしで動く場合もある:
NATURAL JOIN— 同じ名前のカラムを自動で結合してくれる。USING— 結合するカラム名をリストで指定する。CROSS JOIN— いつも条件なし。つまりデカルト積と同じ。
ミス2: 結合条件の間違い
たまに結合条件は書くけど、間違ったカラムで結合しちゃうことがある。たとえば、キーじゃなくて関係ないカラムで結合しちゃうとか。
例えば、学生と彼らが登録してるコースのリストを取りたいのに、間違えて無関係なカラムで結合しちゃう:
SELECT *
FROM students
JOIN courses
ON students.student_id = courses.course_id;
このクエリだと、student_idとcourse_idは全然違うものだから、正しい結果にならない。
どう直すか:ちゃんと正しいカラムで結合してるか確認しよう。正しい結合はこんな感じ(もしenrollmentsテーブルがあって、学生とコースをつないでる場合):
SELECT students.name, courses.course_name
FROM students
JOIN enrollments ON students.student_id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.course_id;
ミス3: 結果の重複行
クエリに複数のJOINを追加すると、たまに行が重複しちゃうことがある。これは、JOINするテーブルに重複データがあったり、結合条件が間違ってる場合に起きる。
例えば、学生Ottoがenrollmentsテーブルで同じコースに2回登録されてるとする。
enrollmentsのレコード:
| student_id | course_id |
|---|---|
| 1 | 101 |
| 1 | 101 |
この状態でJOINすると、こうなる:
SELECT students.name, courses.course_name
FROM students
JOIN enrollments ON students.student_id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.course_id;
結果:
| name | course_name |
|---|---|
| Otto | 数学 |
| Otto | 数学 |
どう直すか:まず、テーブルに重複データがないか確認しよう。もしこれが想定通りなら、DISTINCTで重複を消せる:
SELECT DISTINCT students.name, courses.course_name
FROM students
JOIN enrollments ON students.student_id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.course_id;
ミス4: INNER JOINで行が消える
INNER JOINは、両方のテーブルで一致する行だけ返す。どっちかのテーブルに対応する値がなかったら、その行は消えちゃう。結合タイプを間違えると、データが消えることもある。
例えば、まだどのコースにも登録してない学生がいるとする:
学生 (students):
| student_id | name |
|---|---|
| 1 | Otto |
| 2 | Anna |
| 3 | Dhany |
登録 (enrollments):
| student_id | course_id |
|---|---|
| 1 | 101 |
| 2 | 102 |
この状態でINNER JOINすると:
SELECT students.name, courses.course_name
FROM students
JOIN enrollments ON students.student_id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.course_id;
結果:
| name | course_name |
|---|---|
| Otto | 数学 |
| Anna | 歴史 |
あれ、Dhanyはどこ?コースに登録してない学生も含めたいなら、LEFT JOINを使おう:
SELECT students.name, courses.course_name
FROM students
LEFT JOIN enrollments ON students.student_id = enrollments.student_id
LEFT JOIN courses ON enrollments.course_id = courses.course_id;
ミス5: NULL値の扱いミス
どっちかのテーブルにNULL(空)の値があると、(特にフィルタ条件を使うと)結果から外れちゃうことがある。
例:LEFT JOINを使ってるのに、WHEREでフィルタしちゃう場合。
SELECT students.name, courses.course_name
FROM students
LEFT JOIN enrollments ON students.student_id = enrollments.student_id
LEFT JOIN courses ON enrollments.course_id = courses.course_id
WHERE courses.course_name = '数学';
これだと、コースに登録してない学生は結果に出てこない。せっかくLEFT JOIN使ったのにね。
どう直すか:コースがない行も含めたいなら、WHEREじゃなくてONで条件を書くか、追加条件をこうやって書こう:
SELECT students.name, courses.course_name
FROM students
LEFT JOIN enrollments ON students.student_id = enrollments.student_id
LEFT JOIN courses ON enrollments.course_id = courses.course_id
WHERE courses.course_name IS NULL OR courses.course_name = '数学';
ミス6: 結合タイプの混乱
どの結合タイプを使うか混乱しちゃうこともある。たとえば、RIGHT JOINを使ってるけど、テーブルの順番を変えればLEFT JOINで済む場合とか。
混乱しないコツ:
- できれば
LEFT JOINを使おう。直感的で分かりやすい。 - テーブルの順番を変えて、
RIGHT JOINを避けよう。
GO TO FULL VERSION