WHERE f1 IN ('a','b') ≡ WHERE f1='a' OR f1='b' 或 UNION ALL。WHERE f1 IN (SELECT f1 FROM t2 WHERE fx='x') — 子查詢不相關,優化器通常自動轉為 EXISTS,效能相同。WHERE f1 IN (SELECT f1 FROM t2 WHERE t2.fx = t1.fx) — 子查詢引用外部欄位,效能通常不如 EXISTS。習慣建議:除常數列表外,優先用 EXISTS。
| 場景 | 推薦寫法 | 範例 |
|---|---|---|
| 單表單鍵關聯 | IN | SELECT * FROM A WHERE id IN (SELECT id FROM B) |
| 單表多鍵關聯 | EXISTS | SELECT * FROM A WHERE EXISTS (SELECT 1 FROM B WHERE id=A.id AND col1=A.col1) |
| 雙表回傳欄位 | JOIN | SELECT * FROM A LEFT JOIN B ON A.id=B.id |
-- EXISTS
SELECT * FROM sysobjects WHERE EXISTS (SELECT 1 FROM syscolumns WHERE id=syscolumns.id)
-- 掃描 1 次,邏輯讀 3
-- IN
SELECT * FROM sysobjects WHERE id IN (SELECT id FROM syscolumns)
-- 掃描 47 次,邏輯讀 97
EXISTS 完勝 (大表在子查詢時)。
-- EXISTS
SELECT * FROM syscolumns WHERE EXISTS (SELECT 1 FROM sysobjects WHERE id=syscolumns.id)
-- 掃描 1 次,邏輯讀 10,預讀 15
-- IN
SELECT * FROM syscolumns WHERE id IN (SELECT id FROM sysobjects)
-- 掃描 1 次,邏輯讀 10,無預讀
小表在子查詢時兩者接近,EXISTS 多預讀開銷。
-- NOT EXISTS 找每組最大 sid (600 萬列)
SELECT * FROM test a
WHERE NOT EXISTS (SELECT 1 FROM test WHERE sort=a.sort AND sid > a.sid)
-- 效率較 IN (MAX) 高 3 倍以上
-- 找重複值
-- IN (獨立子查詢,優化器緩存結果集)
SELECT * FROM @t WHERE v IN (SELECT v FROM @t GROUP BY v HAVING COUNT(*)>1)
-- EXISTS (相關子查詢,逐列執行)
SELECT * FROM @t a WHERE EXISTS (SELECT 1 FROM @t WHERE id!=a.id AND v=a.v)
獨立子查詢 IN 會被優化為「結果集緩存 + 檢查」,反而比逐列執行的 EXISTS 快。結論:視子查詢是否相關而定,不可一概而論。
IN 可讀性佳。EXISTS。JOIN。EXPLAIN / Visual Explain 驗證實際計劃。