Tìm cái KHÔNG tồn tại: ba cách và cái nào đúng

Trung cấp 5 phút đọc Cập nhật 30/07/2026 UPA AI Partner

“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ọ.

✗ INNER JOIN✓ LEFT JOIN + IS NULLtenso_donAn3Bình1Chi biến mấttenso_donAn3Bình1Chi0Chi hiện ra với 0

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 EXISTS là lựa chọn mặc định: nhanh và an toàn với NULL.
  • NOT IN gặp một NULL là trả về rỗng hoàn toàn.
  • LEFT JOIN … IS NULL dễ đọ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 →