【問題處理】“NOT IN”與“NULL”的邂逅
今天處理了一個因“NOT IN”與“NULL”邂逅導致的問題,值得思考和總結,記錄在此,供參考。(感謝John丟擲的問題)
我們以實驗的形式先再現一下這個問題,然後對其分析,最後給出幾種解決方案。
1.建立實驗表T,並分別初始化三條資料,注意T2表中包含一條空(NULL)資料。
sec@ora10g> insert into t1 values (1);
sec@ora10g> insert into t1 values (2);
sec@ora10g> insert into t1 values (3);
sec@ora10g> insert into t2 values (1);
sec@ora10g> insert into t2 values (null);
sec@ora10g> insert into t2 values (3);
sec@ora10g> commit;
2.確認T1表和T2表的資料內容
sec@ora10g> select * from t1;
X
----------
1
2
3
sec@ora10g> select * from t2;
X
----------
1
3
3.再現問題
嘗試使用“NOT IN”方法獲得不在T2表中出現的T1表資料。
sec@ora10g> select * from t1 where x not in (select x from t2);
no rows selected
問題已重現,明明T1中的資料“2”在T2表中沒有,為什麼沒有返回結果呢?
原因:當子查詢返回含有“NULL”記錄時,使用NOT IN時將不會有返回記錄。
解析:可以這麼理解這個現象:Oracle中的NULL是一個不確定的狀態。以T1表中的記錄“2”為例,在與T2表中的NULL進行比較時,NULL既可以是“2”,也可以不是“2”,因為判斷不了他們的關係,所以只能返回空記錄,此乃無奈之舉。
4.為什麼使用“IN”可以返回“正確”的結果?
sec@ora10g> select * from t1 where x in (select x from t2);
X
----------
1
3
這是顯然的,因為T2表中確確實實的存在著記錄“1”和記錄“3”,因此“1”和“3”被返回。不過,注意,NULL依然是一個不確定的狀態,因此在T1表中的“2”與NULL比較之後仍然是個不確定的結果,因此“2”是不會被返回的。這裡給我們的一個錯覺:T1表中的“2”與T2表中NULL不同(他們其實也可能相同)。
5.諸多解決方案
1)排除“NOT IN”子查詢中存在的NULL值
sec@ora10g> select * from t1 where x not in (select x from t2 where x is not null);
X
----------
2
2)使用“NOT EXISTS”改寫
sec@ora10g> select * from t1 where not exists (select * from t2 where t1.x=t2.x);
X
----------
2
3)使用“外連線”改寫
sec@ora10g> select t1.* from t1, t2 where t1.x = t2.x(+) and t2.x is null;
X
----------
2
道理是相通的,想想為什麼使用“NOT EXISTS”和“外連線”方法改寫後就可以成功?(給大家一個機會,這裡不贅述。)
6.小結
這裡描述的的案例可以用“陷阱”來形容,不過對於這個“陷阱”我們是有諸多解決方案進行規避的。
遇到問題在沉著、冷靜+淡定之後,終有柳暗花明之時。希望您也有思考之後豁然開朗之悅。
Good luck.
secooler
10.02.09
-- The End --
我們以實驗的形式先再現一下這個問題,然後對其分析,最後給出幾種解決方案。
1.建立實驗表T,並分別初始化三條資料,注意T2表中包含一條空(NULL)資料。
sec@ora10g> insert into t1 values (1);
sec@ora10g> insert into t1 values (2);
sec@ora10g> insert into t1 values (3);
sec@ora10g> insert into t2 values (1);
sec@ora10g> insert into t2 values (null);
sec@ora10g> insert into t2 values (3);
sec@ora10g> commit;
2.確認T1表和T2表的資料內容
sec@ora10g> select * from t1;
X
----------
1
2
3
sec@ora10g> select * from t2;
X
----------
1
3
3.再現問題
嘗試使用“NOT IN”方法獲得不在T2表中出現的T1表資料。
sec@ora10g> select * from t1 where x not in (select x from t2);
no rows selected
問題已重現,明明T1中的資料“2”在T2表中沒有,為什麼沒有返回結果呢?
原因:當子查詢返回含有“NULL”記錄時,使用NOT IN時將不會有返回記錄。
解析:可以這麼理解這個現象:Oracle中的NULL是一個不確定的狀態。以T1表中的記錄“2”為例,在與T2表中的NULL進行比較時,NULL既可以是“2”,也可以不是“2”,因為判斷不了他們的關係,所以只能返回空記錄,此乃無奈之舉。
4.為什麼使用“IN”可以返回“正確”的結果?
sec@ora10g> select * from t1 where x in (select x from t2);
X
----------
1
3
這是顯然的,因為T2表中確確實實的存在著記錄“1”和記錄“3”,因此“1”和“3”被返回。不過,注意,NULL依然是一個不確定的狀態,因此在T1表中的“2”與NULL比較之後仍然是個不確定的結果,因此“2”是不會被返回的。這裡給我們的一個錯覺:T1表中的“2”與T2表中NULL不同(他們其實也可能相同)。
5.諸多解決方案
1)排除“NOT IN”子查詢中存在的NULL值
sec@ora10g> select * from t1 where x not in (select x from t2 where x is not null);
X
----------
2
2)使用“NOT EXISTS”改寫
sec@ora10g> select * from t1 where not exists (select * from t2 where t1.x=t2.x);
X
----------
2
3)使用“外連線”改寫
sec@ora10g> select t1.* from t1, t2 where t1.x = t2.x(+) and t2.x is null;
X
----------
2
道理是相通的,想想為什麼使用“NOT EXISTS”和“外連線”方法改寫後就可以成功?(給大家一個機會,這裡不贅述。)
6.小結
這裡描述的的案例可以用“陷阱”來形容,不過對於這個“陷阱”我們是有諸多解決方案進行規避的。
遇到問題在沉著、冷靜+淡定之後,終有柳暗花明之時。希望您也有思考之後豁然開朗之悅。
Good luck.
secooler
10.02.09
-- The End --
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/519536/viewspace-627169/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- “NOT_IN”與“NULL”的邂逅Null
- Oracle空串與null的處理OracleNull
- crontab 問題檢查與處理
- Lotus notes問題與處理
- 處理問題的方法
- xml處理的問題XML
- job處理緩慢的效能問題排查與分析
- mysql的處理能力問題MySql
- 一個NBU問題的處理
- mysql的處理能力問題(2)MySql
- windows的一個問題處理Windows
- perl中文處理問題
- 漢字處理問題?
- 貨品問題處理
- [git] git問題處理Git
- MYSQL timestamp NOT NULL插入NULL的報錯問題MySqlNull
- sql中的安全問題nullSQLNull
- Java--- 關於null的處理若干方法JavaNull
- Oracle唯一約束中NULL的處理OracleNull
- Oracle 排序中常用的NULL值處理方法Oracle排序Null
- 【C#】-對於Null值的處理方法C#Null
- .net異常處理的效能問題
- GridLayout的使用及問題處理
- 一次efi的問題處理
- enq: HW - contention 問題的處理ENQ
- CRS-2409問題的處理
- weblogic中例外處理的問題Web
- golang json處理問題GolangJSON
- 併發問題處理方式
- ASMCMD處理問題一則ASM
- RMAN處理split block問題BloC
- mysql問題處理兩則MySql
- Oracle啟動問題處理Oracle
- mysql 問題處理二則MySql
- Oracle壞塊問題處理Oracle
- 資料處理--pandas問題
- 【問題處理】MySQL忘記root密碼的處理辦法MySql密碼
- 處理表的行遷移的問題