EXISTS hay IN? Câu trả lời đúng khiến truy vấn nhanh gấp 30 lần

Nâng cao 5 phút đọc Cập nhật 30/07/2026 UPA AI Partner

Hai câu cho cùng kết quả. Một chạy 0,1 giây, một chạy 3 giây. Khác nhau ở một từ khoá.

Bài toán

Tìm khách hàng đã từng đặt ít nhất một đơn. Ba cách viết đều đúng, nhưng chi phí rất khác nhau.

CHI PHÍcách viếtdừng khiINduyệt hết bảng conEXISTStìm thấy dòng đầuJOINghép hết rồi khử trùngCHỌN CÁI NÀOtình huốngnên dùngKiểm tra tồn tạiEXISTSDanh sách ngắn cố địnhINCần cột bên phảiJOIN

Ba cách

-- 1. IN
SELECT * FROM khach_hang
WHERE id IN (SELECT khach_hang_id FROM don_hang);

-- 2. EXISTS
SELECT * FROM khach_hang k
WHERE EXISTS (SELECT 1 FROM don_hang d WHERE d.khach_hang_id = k.id);

-- 3. JOIN + DISTINCT
SELECT DISTINCT k.* FROM khach_hang k
JOIN don_hang d ON d.khach_hang_id = k.id;

Khác biệt cốt lõi

EXISTS dừng ngay khi tìm thấy dòng đầu tiên. Một khách có 500 đơn thì nó vẫn chỉ đọc một dòng. IN phải dựng xong toàn bộ danh sách bên trong rồi mới so.

JOIN + DISTINCT tệ nhất: nó nhân bản 500 dòng rồi mới khử trùng lặp.

Quy tắc chọn Chỉ cần biết “có hay không” thì dùng EXISTS. Cần lấy cột từ bảng bên phải thì mới dùng JOIN. Danh sách ngắn cố định thì IN đọc dễ hơn.

Lỗi thường gặp

1. NOT IN gặp NULL

-- Trả về RỖNG nếu bảng con có một NULL
WHERE id NOT IN (SELECT khach_hang_id FROM don_hang);

-- Luôn an toàn
WHERE NOT EXISTS (SELECT 1 FROM don_hang d WHERE d.khach_hang_id = k.id);

2. Viết SELECT * trong EXISTS

SELECT 1 hay SELECT * đều như nhau về hiệu năng — bộ tối ưu hoá bỏ qua danh sách cột. Nhưng SELECT 1 nói rõ ý định: “tôi không lấy dữ liệu, tôi chỉ kiểm tra”.

3. Quên điều kiện liên kết

-- Luôn đúng với mọi khách, vì bảng con không phụ thuộc k
WHERE EXISTS (SELECT 1 FROM don_hang);

EXISTS phải là subquery tương quan — bên trong bắt buộc tham chiếu tới bảng bên ngoài.

Luyện tập

  • LeetCode 1050 — Actors and Directors Who Cooperated At Least Three Times
  • LeetCode 1148 — Article Views I
  • LeetCode 1517 — Find Users With Valid E-Mails
  • LeetCode 619 — Biggest Single Number

Tóm lại

  • EXISTS dừng ở dòng đầu tiên tìm thấy; IN dựng hết danh sách.
  • Kiểm tra tồn tại thì dùng EXISTS, lấy dữ liệu thì dùng JOIN.
  • NOT IN gặp NULL trả về rỗng — luôn thay bằng NOT EXISTS.
  • JOIN + DISTINCT là cách chậm nhất trong ba cách.
  • EXISTS phải tham chiếu tới bảng bên ngoài mới có nghĩa.

Bạn vừa học xong EXISTS và IN

Sẵn sàng luyện tập chưa?

Làm bài tập EXISTS và IN 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 →