CodeGym /コース /SQL SELF /JOINを使うときによくあるミス

JOINを使うときによくあるミス

SQL SELF
レベル 12 , レッスン 4
使用可能

さて、そろそろ人生について語る時間だね。避けられない側面、それはミス。ミスを見つけて直して理解するのは、データを扱う上で絶対に必要なこと。じゃあ、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つ、JOINONなしで動く場合もある:

  • NATURAL JOIN — 同じ名前のカラムを自動で結合してくれる。
  • USING — 結合するカラム名をリストで指定する。
  • CROSS JOIN — いつも条件なし。つまりデカルト積と同じ。

ミス2: 結合条件の間違い

たまに結合条件は書くけど、間違ったカラムで結合しちゃうことがある。たとえば、キーじゃなくて関係ないカラムで結合しちゃうとか。

例えば、学生と彼らが登録してるコースのリストを取りたいのに、間違えて無関係なカラムで結合しちゃう:

SELECT *
FROM students
JOIN courses
ON students.student_id = courses.course_id;

このクエリだと、student_idcourse_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を避けよう。
1
アンケート/クイズ
複数のJOIN、レベル 12、レッスン 4
使用不可
複数のJOIN
複数のJOIN
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION