Các toán tử IN và NOT IN thường được sử dụng trong SQL, nhưng chúng tiềm ẩn nhiều rủi ro về hiệu năng và độ chính xác. Dưới đây là lý do tại sao bạn nên hạn chế dùng chúng và cách thay thế hiệu quả hơn.
1. Hiệu suất kém
Khi dữ liệu lớn, NOT IN có thể gây ra chậm trễ đáng kể do cách xử lý con truy vấn và so sánh từng giá trị. Trong một số trường hợp, truy vấn có thể không tận dụng được chỉ mục, dẫn đến quét toàn bảng.
2. Dễ gây lỗi logic hoặc trả về kết quả sai
Xét ví dụ sau:
CREATE TABLE bang_a (ma_a INT);
CREATE TABLE bang_b (ma_b INT);
INSERT INTO bang_a (ma_a) VALUES (10), (20), (30);
INSERT INTO bang_b (ma_b) VALUES (10), (20);
Mục tiêu: Lấy các giá trị ma_a từ bang_a mà tồn tại trong bang_b.
-- Cách viết dễ mắc lỗi:
SELECT ma_a FROM bang_a
WHERE ma_a IN (SELECT ma_a FROM bang_b); -- Sai cột!
Dù SELECT ma_a FROM bang_b sẽ báo lỗi khi chạy riêng, nhưng khi nằm trong IN, SQL Server lại ngầm hiểu là tham chiếu đến cột ngoài — dẫn đến kết quả sai mà không cảnh báo.
Trường hợp nghiêm trọng hơn với NOT IN:
INSERT INTO bang_b (ma_b) VALUES (NULL);
-- Truy vấn mong muốn: lấy giá trị 30
SELECT ma_a FROM bang_a
WHERE ma_a NOT IN (SELECT ma_b FROM bang_b);
Kết quả trả về: trống.
Lý do: NOT IN trả về UNKNOWN nếu bất kỳ giá trị nào trong danh sách là NULL, do quy tắc 3-valued logic của SQL. Vì vậy, điều kiện luôn bị loại bỏ.
Lưu ý: Hạn chế cho phép
NULLtrong các cột dùng để so sánh, đặc biệt là khóa ngoại hoặc điều kiện lọc.
Giải pháp thay thế
1. Dùng EXISTS / NOT EXISTS
SELECT * FROM bang_a a
WHERE EXISTS (SELECT 1 FROM bang_b b WHERE b.ma_b = a.ma_a);
SELECT * FROM bang_a a
WHERE NOT EXISTS (SELECT 1 FROM bang_b b WHERE b.ma_b = a.ma_a);
2. Dùng JOIN
-- Tương đương IN
SELECT DISTINCT a.ma_a
FROM bang_a a
INNER JOIN bang_b b ON a.ma_a = b.ma_b;
-- Tương đương NOT IN
SELECT a.ma_a
FROM bang_a a
LEFT JOIN bang_b b ON a.ma_a = b.ma_b
WHERE b.ma_b IS NULL;
Cả hai cách trên đều an toàn với NULL, dễ tối ưu bởi engine, và ít nhạy cảm với lỗi cú pháp.
Ghi chú: Vẫn có thể dùng
INnếu danh sách là cố định và nhỏ, ví dụ:WHERE status IN ('active', 'pending').