“Khách nào chưa từng mua hàng?” — câu hỏi này loại nhiều ứng viên hơn bất kỳ câu JOIN nào khác.
Bài toán
Tìm khách hàng chưa từng đặt đơn nào. Vấn đề: những khách này không có dòng nào trong bảng đơn hàng, nên JOIN thông thường không thấy họ.
Ba cách viết
1. LEFT JOIN … IS NULL
SELECT k.*
FROM khach_hang k
LEFT JOIN don_hang d ON d.khach_hang_id = k.id
WHERE d.id IS NULL;
Giữ hết khách, rồi giữ lại những dòng mà bên phải rỗng. Trực quan nhất khi mới học.
2. NOT EXISTS — thường nhanh nhất
SELECT k.*
FROM khach_hang k
WHERE NOT EXISTS (
SELECT 1 FROM don_hang d WHERE d.khach_hang_id = k.id
);
Máy dừng ngay khi tìm thấy dòng đầu tiên, không cần ghép hết. An toàn với NULL.
3. NOT IN — cách nguy hiểm
SELECT * FROM khach_hang
WHERE id NOT IN (SELECT khach_hang_id FROM don_hang);
Bẫy chết người
Nếu bảng con trả về dù chỉ một giá trị
NULL, toàn bộ câu truy vấn trả về rỗng. Không lỗi, không cảnh báo, chỉ là không có dòng nào.
Lý do: id NOT IN (1, 2, NULL) tương đương id <> 1 AND id <> 2 AND id <> NULL. Vế cuối luôn cho UNKNOWN, và AND với UNKNOWN không bao giờ thành TRUE.
Nếu buộc phải dùng, chặn lại:
WHERE id NOT IN (
SELECT khach_hang_id FROM don_hang
WHERE khach_hang_id IS NOT NULL
);
Lỗi thường gặp
Đặt điều kiện lọc ở WHERE thay vì ON:
-- SAI: biến LEFT JOIN thành INNER JOIN
LEFT JOIN don_hang d ON d.khach_hang_id = k.id
WHERE d.trang_thai = 'hoan_thanh' AND d.id IS NULL
-- ĐÚNG
LEFT JOIN don_hang d
ON d.khach_hang_id = k.id
AND d.trang_thai = 'hoan_thanh'
WHERE d.id IS NULL
Luyện tập
- LeetCode 183 — Customers Who Never Order
- LeetCode 1607 — Sellers With No Sales
- LeetCode 1795 — Rearrange Products Table
- LeetCode 619 — Biggest Single Number
- HackerRank — Challenges
Tóm lại
- Anti-join là tìm dòng không có đối tượng khớp.
NOT EXISTSlà lựa chọn mặc định: nhanh và an toàn vớiNULL.NOT INgặp mộtNULLlà trả về rỗng hoàn toàn.LEFT JOIN … IS NULLdễ đọc nhất khi mới học.- Điều kiện lọc bảng bên phải phải nằm ở
ON.
Bạn vừa học xong anti-join
Sẵn sàng luyện tập chưa?
Làm bài tập anti-join trên dữ liệu thật, chấm điểm ngay khi bạn bấm chạy.
Bắt đầu luyện tập →