Bây giờ là lúc nói về cuộc sống. Về mặt mà ai cũng phải gặp: lỗi. Bắt lỗi, sửa lỗi và hiểu lỗi – đó là phần không thể thiếu khi làm việc với dữ liệu. Cùng xem thử những cái bẫy nào hay gặp khi dùng JOIN trong SQL, và làm sao để né chúng nhé.
Lỗi 1: Bỏ qua điều kiện nối – tạo ra phép nhân Descartes
Lỗi phổ biến nhất – quên chỉ định điều kiện nối khi dùng ON. Khi đó sẽ xảy ra phép nhân Descartes, tức là mỗi dòng của bảng đầu sẽ kết hợp với mỗi dòng của bảng thứ hai. Kết quả là bạn nhận được một đống dòng vô nghĩa, chỉ làm bạn rối thêm thôi.
Lấy ví dụ nhé. Giả sử mình có các bảng sau:
Sinh viên (students):
| student_id | name |
|---|---|
| 1 | Otto |
| 2 | Anna |
Khoá học (courses):
| course_id | course_name |
|---|---|
| 101 | Toán học |
| 102 | Lịch sử |
Giờ mình viết truy vấn mà quên mất ON:
SELECT *
FROM students
JOIN courses;
Kết quả:
| student_id | name | course_id | course_name |
|---|---|---|---|
| 1 | Otto | 101 | Toán học |
| 1 | Otto | 102 | Lịch sử |
| 2 | Anna | 101 | Toán học |
| 2 | Anna | 102 | Lịch sử |
Nhìn không đúng chút nào nhỉ? Cơn ác mộng này gọi là phép nhân Descartes.
Cách sửa: dùng ON để chỉ rõ dữ liệu liên kết giữa các bảng như thế nào.
SELECT *
FROM students
JOIN courses
ON students.student_id = courses.course_id;
Và đây lại mở ra một chương mới của lỗi...
Bảo vệ khỏi "gà mờ"
Vấn đề này phổ biến đến mức PostgreSQL đã cấm dùng JOIN mà không có ON và điều kiện.
Nếu bạn thực sự muốn kết hợp mọi dòng với mọi dòng, có thể dùng cú pháp không cần JOIN:
SELECT *
FROM students, courses;
Thêm một trường hợp nữa – khi JOIN không có ON vẫn chạy được:
- Với
NATURAL JOIN– tự động ghép các cột trùng tên. - Với
USING– bạn chỉ định danh sách cột để ghép. CROSS JOIN– luôn không có điều kiện – cũng là phép nhân Descartes.
Lỗi 2: Điều kiện nối sai
Đôi khi bạn có điều kiện nối, nhưng lại sai. Ví dụ, nối bảng không phải bằng khoá mà bằng dữ liệu không liên quan.
Giả sử bạn muốn lấy danh sách sinh viên và khoá học mà họ đăng ký, nhưng lại nối bảng bằng các trường không liên quan:
SELECT *
FROM students
JOIN courses
ON students.student_id = courses.course_id;
Truy vấn này sẽ cho ra kết quả sai, vì student_id và course_id là hai thứ hoàn toàn khác nhau.
Cách sửa: hãy chắc chắn bạn dùng đúng cột để nối. Nối đúng có thể như sau (nếu bạn có bảng enrollments liên kết sinh viên với khoá học):
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;
Lỗi 3: Trùng lặp dòng trong kết quả
Khi bạn thêm nhiều JOIN vào truy vấn, đôi khi sẽ bị trùng lặp dòng. Điều này xảy ra nếu trong các bảng JOIN có dữ liệu lặp lại, hoặc bạn chỉ định điều kiện nối sai.
Ví dụ, sinh viên Otto đăng ký hai lần cùng một khoá học trong bảng enrollments.
Dữ liệu trong enrollments:
| student_id | course_id |
|---|---|
| 1 | 101 |
| 1 | 101 |
Giờ truy vấn với JOIN sẽ ra kết quả như sau:
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;
Kết quả:
| name | course_name |
|---|---|
| Otto | Toán học |
| Otto | Toán học |
Cách sửa: đầu tiên, hãy chắc chắn bảng của bạn không có dữ liệu trùng lặp. Thứ hai, nếu đây là hành vi mong muốn, hãy loại bỏ dòng trùng bằng 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;
Lỗi 4: Mất dòng khi dùng INNER JOIN
INNER JOIN chỉ trả về những dòng khớp ở cả hai bảng. Nếu một bảng không có giá trị tương ứng, dòng đó sẽ bị loại bỏ. Bạn có thể mất dữ liệu nếu chọn sai loại nối.
Giả sử mình có một sinh viên chưa đăng ký khoá học nào:
Sinh viên (students):
| student_id | name |
|---|---|
| 1 | Otto |
| 2 | Anna |
| 3 | Dhany |
Đăng ký (enrollments):
| student_id | course_id |
|---|---|
| 1 | 101 |
| 2 | 102 |
Giờ truy vấn với 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;
Kết quả:
| name | course_name |
|---|---|
| Otto | Toán học |
| Anna | Lịch sử |
Thế còn Dhany đâu? Nếu bạn muốn cả sinh viên chưa có khoá học cũng xuất hiện, hãy dùng 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;
Lỗi 5: Xử lý sai giá trị NULL
Nếu một trong các bảng có dòng với giá trị rỗng (NULL), chúng có thể bị loại khỏi kết quả (ví dụ khi dùng điều kiện lọc).
Ví dụ: bạn dùng LEFT JOIN nhưng lại thêm WHERE để lọc.
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 = 'Toán học';
Bây giờ sinh viên không có khoá học sẽ không xuất hiện trong kết quả, dù bạn đã dùng LEFT JOIN.
Cách sửa: nếu muốn bao gồm cả dòng không có khoá học, hãy thay WHERE bằng ON hoặc thêm điều kiện nữa:
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 = 'Toán học';
Lỗi 6: Nhầm lẫn giữa các loại nối
Bạn bị rối không biết dùng loại nối nào. Ví dụ, dùng RIGHT JOIN trong khi chỉ cần LEFT JOIN và đảo thứ tự bảng là được.
Cách tránh nhầm lẫn:
- Dùng
LEFT JOINnếu có thể. Nó dễ hiểu hơn nhiều. - Đổi thứ tự bảng để khỏi phải dùng
RIGHT JOIN.
GO TO FULL VERSION