| 場景 | 推薦寫法 | 核心原因 |
|---|---|---|
| 子查詢結果集 較小 | IN | Hash Join 效率高,內表小則構建雜湊表快 |
| 子查詢結果集 較大、外表 較小 | EXISTS | 採用 Nested Loop,外表驅動,適合外小內大 |
| NOT IN vs NOT EXISTS | 永遠用 NOT EXISTS | NOT IN 全表掃描且不走索引;NOT EXISTS 可利用索引 |
| 含 NULL 值風險 | NOT EXISTS | NOT IN 遇 NULL 邏輯失效(結果為空) |
-- 危險範例:子查詢含 NULL 導致無結果
SELECT * FROM t1 WHERE id NOT IN (SELECT id FROM t2); -- 若 t2.id 有 NULL,全表無結果
原因:NULL 參與比較結果為 UNKNOWN,導致整體判斷失效。且 NOT IN 通常退化為全表掃描 + Filter,無法利用索引。
-- 推薦寫法:語義精確、可用索引、免疫 NULL
SELECT * FROM t1 WHERE NOT EXISTS (SELECT 1 FROM t2 WHERE t2.id = t1.id);
t2.id 索引。ANTI JOIN(Hash Join Anti 或 Nested Loop Anti)。/*+ UNNEST */ 或 /*+ NO_UNNEST */ Hint 強制/禁止子查詢展開為連接。IN (SELECT ...) 等價於 EXISTS (SELECT 1 FROM ... WHERE ... = outer.col),優化器常自動轉換。「NOT IN 棄用,NOT EXISTS 首選」是鐵律。肯定查詢視數據量分佈與索引情況靈活選擇,執行計畫才是唯一標準,切勿迷信經驗法則。