CodeGym /課程 /SQL SELF /JOIN 裡加上額外條件: ON ... AND ...

JOIN 裡加上額外條件: ON ... AND ...

SQL SELF
等級 12 , 課堂 2
開放

你應該已經知道怎麼用 JOIN 來合併資料表了。但現實世界裡,只靠 key 對上還不夠。常常你會遇到這種需求:合併資料時,還要符合額外條件,像是只要活躍的紀錄、只要今年的資料、或只要已完成的訂單。

這時候就要用 ON 加上 AND 來擴充條件啦。

JOIN ... ON 裡加上額外條件,可以讓你精準控制哪些 row 會被 join,而且是在 SQL 開始產生結果之前就先過濾。這樣寫查詢:

  • 更快(經過 JOIN 的 row 變少了)
  • 更精準(過濾直接在 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 裡面,這樣 join 的時候只會連到 active 的紀錄,而不是合併完再過濾。

為什麼不用 WHERE

你也可以這樣寫:

...
WHERE enrollments.status = 'active'

但這樣 LEFT JOIN 時行為會不一樣WHERE 過濾會把沒對到的 row(NULL)都砍掉,這樣 LEFT JOIN 就變成 INNER JOIN 了。

AND enrollments.status = 'active' 放在 ON 裡,會直接限制 join 的 row —— 決定哪些 row 會進 join,不是合併完才過濾。

這種寫法很重要,尤其你想保留某個表的 row,即使另一個表沒對到值(報表、分析時很常見)。

更多 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;

這裡我們只 join 跟今年有關的紀錄。

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'

我們在 join 階段 就把退選的學生排除了,不是合併完才過濾。

name name
Otto Song Math
Otto Song CS
Alex Lin Math

條件寫在 ON,PostgreSQL 可以優化 join 計劃,處理更少 row。這在資料量很大時超重要。內部過濾比 join 後再砍掉還有效率。

JOIN ON 不只是 key 對 key

很多人以為 ON 只能寫 id = 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 != 'Физкультура'
JOIN faculties f
    ON c.faculty_id = f.id
    AND f.name IS NOT NULL;

這裡我們同時過濾:

  • active 的紀錄,
  • 課程不是「Физкультура」的,
  • faculty 有名字的。

查詢結果:

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