IN 與 EXISTS 常用於子查詢,但優化策略不同,效能表現隨資料分布、索引、NULL 值而異。本文透過實測說明核心差異。
| 場景 | 建議 | 原因 |
|---|---|---|
| NOT IN vs NOT EXISTS | 優先用 NOT EXISTS | NOT IN 若子查詢含 NULL 則無列回傳;NOT EXISTS 可用連接演算法 |
| IN vs EXISTS | 小表驅動大表用 IN,大表驅動小表用 EXISTS | IN 傾向轉為 JOIN(Hash/merge join);EXISTS 採用相關子查詢(Nested Loops) |
| 子查詢回傳大量列 | EXISTS | EXISTS 只要找到一筆即停止,不需完整掃描 |
CREATE TABLE #tempTable1 (argument1 NVARCHAR(50), argument2 VARCHAR(20), argument3 DATETIME, argument4 INT); CREATE TABLE #tempTable2 (argument1 NVARCHAR(50), argument2 VARCHAR(20), argument3 DATETIME, argument4 INT); -- 插入測試資料(略,見原文)
-- IN 寫法 SELECT * FROM #tempTable2 WHERE argument1 IN (SELECT argument1 FROM #tempTable1); -- EXISTS 寫法 SELECT * FROM #tempTable2 t2 WHERE EXISTS (SELECT 1 FROM #tempTable1 t1 WHERE t1.argument1 = t2.argument1);
兩者執行計畫通常相同(Hash Match 或 Merge Join),效能接近。
-- NOT IN:若子查詢含 NULL,整個結果為空!極危險 SELECT * FROM #tempTable1 WHERE argument1 NOT IN (SELECT argument1 FROM #tempTable2); -- NOT EXISTS:安全、可用索引、效能穩定 SELECT * FROM #tempTable1 t1 WHERE NOT EXISTS (SELECT 1 FROM #tempTable2 t2 WHERE t1.argument1 = t2.argument1);
SELECT * FROM t1 WHERE col NOT IN (SELECT col FROM t2); -- 若 t2.col 有任一 NULL,結果永遠為 0 列
NOT EXISTS 不受 NULL 影響,語意更直觀。
IN,大表驅動小表用 EXISTS。SELECT 1 而非 SELECT *,減少元資料開銷。