CodeGym /コース /SQL SELF /JOINの追加条件: ON ... AND ...

JOINの追加条件: ON ... AND ...

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

もうJOINでテーブルを結合するやり方は知ってるよね。でも実際の現場だとキーが一致するだけじゃ足りないことが多いんだ。たとえば、追加の条件を満たすデータだけ結合したいってことがよくある。例えば、アクティブなレコードだけとか、今年のデータだけとか、完了した注文だけとかね。

そこでONANDを追加して拡張する出番!

JOIN ... ONの追加条件を使うと、どの行を結合に使うかを細かくコントロールできる。SQLが結果を作る前にフィルタできるから、クエリが:

  • 速くなる(JOINを通る行が減る)、
  • 正確になる(結合時点でフィルタリング)、
  • LEFT JOINで予測しやすくなる(WHEREでのフィルタと違って)。

例:アクティブなコース登録だけ

たとえば、enrollmentsテーブルには学生の参加ステータスが入ってる:activedroppedpending

studentsテーブル:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

enrollmentsテーブルを更新:

student_id course_id status
1 101 active
1 103 active
2 102 dropped
3 101 active

coursesテーブル:

id name
101 Mathematics
102 Physics
103 Computer Science

今、アクティブなコースを持ってる学生だけを取得したい:

SELECT 
    students.name AS student_name,
    courses.name AS course_name
FROM students 
INNER JOIN enrollments 
    ON students.id = enrollments.student_id
    AND enrollments.status = 'active'
INNER JOIN courses 
    ON enrollments.course_id = courses.id;

結果:

student_name course_name
Otto Song Mathematics
Otto Song Computer Science
Alex Lin Mathematics

ここではAND enrollments.status = 'active'ONの中に追加して、アクティブなレコードだけで結合するようにしてる。結合後にフィルタするんじゃなくてね。

なんでWHEREじゃダメ?

こう書くこともできる:

...
WHERE enrollments.status = 'active'

でもこれはLEFT JOINだと動きが違うWHEREでフィルタすると、マッチしない行(NULL)が消えちゃって、LEFT JOININNER JOINみたいになっちゃう。

でもONの中にAND enrollments.status = 'active'を入れると、どの行が結合に入るかを直接コントロールできる。結果をフィルタするんじゃなくて、最初から結合対象を絞る感じ。

このやり方は、もう一方のテーブルに合う値がなくても、片方のテーブルの行を残したいとき(レポートや分析でよくある)に特に大事!

他のON ... AND ...の使い方例

studentsテーブル:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

enrollmentsテーブル:

student_id course_id status enrolled_at
1 101 active 2025-02-01
1 103 active 2025-03-05
2 102 dropped 2024-05-15
3 101 active 2025-03-12

coursesテーブル:

id name
101 Math
102 Physics
103 CS

例:今年のコースだけ

SELECT
    students.name,
    courses.name,
    enrollments.enrolled_at
FROM students
JOIN enrollments
    ON students.id = enrollments.student_id
    AND EXTRACT(YEAR FROM enrollments.enrolled_at) = EXTRACT(YEAR FROM CURRENT_DATE)
JOIN courses
    ON enrollments.course_id = courses.id;

ここでは今年のデータだけ結合してる。

name name enrolled_at
Otto Song Math 2025-02-01
Otto Song CS 2025-03-05
Alex Lin Math 2025-03-12

例:値で除外

JOIN enrollments
    ON students.id = enrollments.student_id
    AND enrollments.status != 'dropped'

退会した学生を結合時点で除外してる。後からフィルタするんじゃなくて。

name name
Otto Song Math
Otto Song CS
Alex Lin Math

条件をONの中に書くと、PostgreSQLは結合プランを最適化できて、処理する行が減る。大量データだと特に重要!JOIN後に「落とす」より、内部でフィルタした方が効率いいよ。

JOIN ONキーだけじゃない

ONid = idだけ」って思ってる人多いけど、実は:

  • 論理演算子:ANDORNOT
  • 比較:><<>BETWEENIN
  • 式:EXTRACTDATE_TRUNCCOALESCENULLIF

全部組み合わせてみよう

studentsテーブル:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

facultiesテーブル:

id name
10 Engineering
20 Natural Sciences
30 ← 名前なし (NULL)

coursesテーブル:

id name teacher faculty_id
101 Math Liam Park 10
102 Physics Chloe Zhang 20
103 CS Noah Kim 10
104 PE Ava Chen 30

enrollmentsテーブル:

student_id course_id status
1 101 active
1 103 active
2 102 dropped
3 101 active
3 104 active
SELECT
    s.name AS student_name,
    c.name AS course_name,
    f.name AS faculty_name
FROM students s
JOIN enrollments e
    ON s.id = e.student_id
    AND e.status = 'active'
JOIN courses c
    ON e.course_id = c.id
    AND c.name != 'PE'
JOIN faculties f
    ON c.faculty_id = f.id
    AND f.name IS NOT NULL;

ここでは同時に:

  • アクティブなレコード、
  • 「PE」以外のコース、
  • 名前がある学部

でフィルタしてる。

クエリの結果:

student_name course_name faculty_name
Otto Song Math Engineering
Otto Song CS Engineering
Alex Lin Math Engineering

このレクチャー、気に入ってくれたら嬉しい!JOINにフィルタを組み合わせて使うのは、これからめっちゃよく出てくるからね。ほぼ毎回使うよ :)

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION