Bài toán
Lấy các dòng mà StudentFirstName KHÔNG nằm trong tập con có StudentScore < 50.
Ví dụ
Tạo bảng:
CREATE TABLE DemoTable625 (
StudentId INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
StudentFirstName VARCHAR(100),
StudentScore INT
);
INSERT INTO DemoTable625(StudentFirstName, StudentScore) VALUES
('John', 98), ('Chris', 39), ('Bob', 41), ('David', 40), ('Robert', 52);
Truy vấn dùng NOT IN + subquery:
SELECT * FROM DemoTable625
WHERE StudentFirstName NOT IN (
SELECT StudentFirstName
FROM DemoTable625
WHERE StudentScore < 50
);
Kết quả: John (98), Robert (52).
Phân tích
- Subquery trả về: 'Chris', 'Bob', 'David' (score < 50).
- Outer query: Loại trừ 3 tên này → giữ John, Robert.
Cách viết thay thế (thường tốt hơn)
LEFT JOIN + IS NULL:
SELECT d1.*
FROM DemoTable625 d1
LEFT JOIN (
SELECT StudentFirstName FROM DemoTable625 WHERE StudentScore < 50
) d2 ON d1.StudentFirstName = d2.StudentFirstName
WHERE d2.StudentFirstName IS NULL;
NOT EXISTS (an toàn với NULL):
SELECT * FROM DemoTable625 d1
WHERE NOT EXISTS (
SELECT 1 FROM DemoTable625 d2
WHERE d2.StudentFirstName = d1.StudentFirstName
AND d2.StudentScore < 50
);
So sánh NOT IN vs NOT EXISTS
| Tiêu chí | NOT IN | NOT EXISTS |
|---|---|---|
| NULL trong subquery | Trả về UNKNOWN (0 dòng) - CỨC KỲ NGUY HIỂM | Xử lý đúng (NULL không match) |
| Hiệu năng | Tương đương (optimizer chuyển đổi) | Tương đương |
| Độ đọc | Đơn giản | Rõ ràng ý định |
Best Practice
- Luôn dùng NOT EXISTS thay vì NOT IN khi subquery có thể trả về NULL.
- Đảm bảo cột so sánh có index.
- Tránh NOT IN với subquery phức tạp, thay bằng LEFT JOIN/IS NULL.