在 Oracle SQL 中,IN/NOT IN、EXISTS/NOT EXISTS 對 NULL 的處理邏輯截然不同,誤用極易導致查詢結果與預期不符。本文通過實測案例深度解析其底層邏輯差異。
核心規則: Oracle 中 NULL 無法與任何值比較(含自身),NULL = NULL 結果為 UNKNOWN,非 TRUE。
SELECT 1 result1 FROM dual WHERE 1 NOT IN (2, 3); -- 返回 1
SELECT 1 result2 FROM dual WHERE 1 NOT IN (2, 3, NULL); -- 無返回
SELECT 1 result3 FROM dual WHERE 1 IN (2, 3, NULL, 1); -- 返回 1
SELECT 1 result4 FROM dual WHERE 1 IN (2, 3, NULL); -- 無返回
a IN (b, c, d) 等價於 (a=b) OR (a=c) OR (a=d)。只要有一個為 TRUE,整體為 TRUE;NULL 導致該項為 UNKNOWN,不影響其他 TRUE 項。a NOT IN (b, c, d) 等價於 (a!=b) AND (a!=c) AND (a!=d)。只要列表中包含 NULL,該項為 UNKNOWN,導致整體 AND 為 UNKNOWN(視同 FALSE),結果永不返回任何行。核心規則: EXISTS 僅判斷子查詢是否返回至少一行,不關心具體值;NOT EXISTS 判斷是否無行返回。
SELECT 1 result5 FROM dual WHERE NOT EXISTS (SELECT 1 FROM dual t WHERE t.dummy = NULL); -- 返回 1
SELECT 1 result6 FROM dual WHERE EXISTS (SELECT 1 FROM dual t WHERE t.dummy = NULL); -- 無返回
WHERE t.dummy = NULL 永無行返回(因 NULL 不可比較)。NOT EXISTS 見到空結果集 → 判斷為 TRUE → 外層返回行。EXISTS 見到空結果集 → 判斷為 FALSE → 外層無返回。
WHERE (a, b) NOT IN ((c, d), (e, f))
-- 等價於 ((a!=c OR b!=d)) AND ((a!=e OR b!=f))
任一座標不匹配即視為該元組不相等。若元組中含 NULL,該元組比較結果為 UNKNOWN,導致整體 AND 為 UNKNOWN。
| 運算子 | 列表含 NULL 時行為 | 關鍵特性 |
|---|---|---|
IN | 若其他值匹配則返回 TRUE,否則 FALSE | OR 邏輯,NULL 不阻斷匹配 |
NOT IN | 永遠返回 FALSE(無行返回) | AND 邏輯,NULL 導致整體 UNKNOWN |
EXISTS | 子查詢無行返回 → FALSE | 只看行是否存在,忽略 NULL 值 |
NOT EXISTS | 子查詢無行返回 → TRUE | 最安全的「不存在」判斷方式 |
WHERE col IS NOT NULL 過濾,或改寫為 NOT EXISTS。NOT IN 可能較快;大結果集、有索引時 NOT EXISTS 通常優勝。建議實測執行計劃決定。