CodeGym /Các khóa học /SQL SELF /Điều kiện bổ sung trong JOIN: ON ......

Điều kiện bổ sung trong JOIN: ON ... AND ...

SQL SELF
Mức độ , Bài học
Có sẵn

Mày chắc cũng biết cách kết nối bảng bằng JOIN rồi nhỉ. Nhưng thực tế thì chỉ so khớp theo key thôi thì chưa đủ đâu. Nhiều lúc cần phải join dữ liệu chỉ khi nó thỏa mãn thêm điều kiện nào đó — ví dụ chỉ lấy record active, chỉ lấy data năm nay hoặc chỉ lấy đơn hàng đã hoàn thành.

Và đây là lúc mở rộng cú pháp ON với AND phát huy tác dụng.

Điều kiện bổ sung trong JOIN ... ON giúp kiểm soát chính xác dòng nào sẽ được join, ngay trước khi SQL build kết quả. Như vậy query sẽ:

  • Nhanh hơn (ít dòng phải đi qua JOIN hơn),
  • Chính xác hơn (lọc ngay lúc join),
  • Dễ đoán hơn khi dùng LEFT JOIN (khác với filter ở WHERE).

Ví dụ: Chỉ lấy record đăng ký khóa học đang active

Giả sử bảng enrollments có trạng thái tham gia của sinh viên: active, dropped, pending.

Bảng students:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

Cập nhật bảng enrollments:

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

Bảng courses:

id name
101 Mathematics
102 Physics
103 Computer Science

Bây giờ mình muốn lấy chỉ sinh viên có khóa học active thôi:

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;

Kết quả:

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

Ở đây mình thêm AND enrollments.status = 'active' vào trong ON, để chỉ join record active, chứ không phải lọc sau khi đã join xong.

Tại sao không dùng WHERE?

Có thể viết như này:

...
WHERE enrollments.status = 'active'

Nhưng nó sẽ khác khi dùng LEFT JOIN. Filter ở WHERE sẽ xóa dòng không có match (NULL), và biến LEFT JOIN thành INNER JOIN luôn.

Còn nếu để AND enrollments.status = 'active' trong ON thì nó sẽ giới hạn luôn dòng được join — kiểm soát dòng nào được join, chứ không chỉ filter kết quả sau đó.

Cách này rất quan trọng nếu mày muốn giữ lại dòng từ một bảng, kể cả khi bảng kia không có giá trị phù hợp (hay gặp trong report, analytic).

Thêm ví dụ dùng ON ... AND ...

Bảng students:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

Bảng 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

Bảng courses:

id name
101 Math
102 Physics
103 CS

Ví dụ: chỉ lấy khóa học năm hiện tại

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;

Ở đây chỉ join record thuộc năm hiện tại thôi.

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

Ví dụ: loại trừ theo giá trị

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

Loại trừ sinh viên đã bỏ học ngay lúc join, chứ không phải filter sau.

name name
Otto Song Math
Otto Song CS
Alex Lin Math

Khi để điều kiện trong ON, PostgreSQL có thể tối ưu hóa plan join và xử lý ít dòng hơn. Cực kỳ quan trọng khi data lớn. Lọc bên trong hiệu quả hơn là "lọc sau" JOIN.

JOIN ONkhông chỉ là key

Nhiều người nghĩ ON chỉ là id = id. Thực ra có thể nhét vào đó:

  • Toán tử logic: AND, OR, NOT
  • So sánh: >, <, <>, BETWEEN, IN
  • Biểu thức: EXTRACT, DATE_TRUNC, COALESCE, NULLIF

Kết hợp tất cả lại

Bảng students:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

Bảng faculties:

id name
10 Engineering
20 Natural Sciences
30 ← khong ten (NULL)

Bảng 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

Bảng 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 != 'Fizkultura'
JOIN faculties f
    ON c.faculty_id = f.id
    AND f.name IS NOT NULL;

Ở đây mình filter cùng lúc theo:

  • Record active,
  • Khóa học không phải "Fizkultura",
  • Khoa có tên.

Kết quả query:

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

Hy vọng mày thích bài giảng này nha. Mày sẽ dùng nhiều JOIN với filter trong query lắm luôn. Gần như lúc nào cũng thế :)

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION