1. 程式人生 > >Oracle not in查不到應有的結果(NULL、IN、EXISTS詳解)

Oracle not in查不到應有的結果(NULL、IN、EXISTS詳解)

from 邏輯運算 zha order .net 提升 特點 where zhang

問題:

語句1 :

Select * from table1 A where A.col1 not in ( select col1 from table2 B )

轉載註明出處:http://x- spirit.iteye.com/、http: //www.blogjava.net/zhangwei217245/
如果這樣,本來應該有一條數據,結果沒有。
如果我改寫成這樣:

語句2 :

select * from table1 A where not exists ( SELECT * FROM table2 B where B.col1 = A.col1)

結果就正確,有一條數據顯示。
轉載註明出處:http://x- spirit.iteye.com/、http: //www.blogjava.net/zhangwei217245/

經過一番搜索,原以為是子查詢結果集太大的原因。


後來有網上強人指點:子查詢裏面有空集。即子查詢的結果集裏面有NULL的結果。


把查詢語句修改成:


語句3 :

Select * from table1 A where A.col1 not in ( select col1 from table2 B where B.col1 is not null )


果然就查出來了。而且一點不差。。。厲害阿~~~




下面是針對本文題的分析:

1。 首先來說說Oracle中的NULL。

Oracle中的NULL代表的是無意義,或者沒有值。將NULL和其他的值進行邏輯運算,運算過程中,NULL的表現更象是FALSE。
下面請看真值表:

AND NULL OR NULL
TRUE NULL TRUE
FALSE FALSE NULL
NULL NULL NULL


另外,NULL和其他的值進行比較或者算術運算(<、>、=、!=、+、-、*、/),結果仍是NULL。

如果想要判定某個值是否為NULL,可以用IS NULL或者IS NOT NULL。

2. 再來說說Oracle中的IN。

in是一個成員條件, 對於給定的一個集合或者子查詢,它會比較每一個成員值。
IN功能上相當於 =ANY 的操作,而NOT IN 功能上相當於 !=ALL 的操作。
IN在邏輯上實際上就是對給定的成員集合或者子查詢結果集進行逐條的判定,例如:

SELECT * FROM table1 A WHERE A.col1 in ( 20 , 50 , NULL );

實際上就是執行了

SELECT * FROM table1 A WHERE A.col1 = 20 OR A.col1 = 50 OR A.col1 = NULL ;

這樣,根據NULL的運算特點和真值表,我們可以看出,上邊這個WHERE 字句可以被簡化(如果返回NULL則無結果集返回,這一點和FALSE是一樣的)為

WHERE A.col1 = 20 OR A.col1 = 50

也就是說,如果你的table1中真的存在含有NULL值的col1列,則執行該語句,無法查詢出那些值為null的記錄。

再來看看NOT IN。根據邏輯運算關系,我們知道,NOT (X=Y OR N=M) 等價於 X!=Y AND N!=M,那麽:

SELECT * FROM table1 A WHERE A.col1 not in ( 20 , 50 , NULL )

等價於

SELECT * FROM table1 A WHERE A.col1 != 20 AND A.col1 != 50 AND A.col1 != NULL

根據NULL的運算特性和真值表,該語句無論前兩個判定條件是否為真,其結果一定是NULL或者FALSE。故絕對沒有任何記錄可以返回。

這就是為什麽語句1 查不到應有結果的原因。當然,如果你用NOT IN的時候,預先在子查詢裏把NULL去掉的話,那就沒問題了,例如語句3 。
有些童鞋可能要問了:那如果我想把A表裏面那些和B表一樣col1列的值一樣的記錄都查出來,即便A、B兩表裏面的col1列都包括值為NULL的記錄的 話,用這一條語句就沒辦法了嗎?

我只能很遺憾的告訴你,如果你想在WHERE後面單純用IN 似乎不太可能了,當然,你可以在外部的查詢語句中將NULL條件並列進去,例如:

SELECT * FROM table1 A WHERE A.col1 in ( SELECT B.col1 FROM table2 B) OR A.col1 IS NULL ;


轉載註明出處:http://x- spirit.iteye.com/、http: //www.blogjava.net/zhangwei217245/

3. 最後談談EXISTS。

有人說EXISTS的性能比IN要好。但這是很片面的。我們來看看EXISTS的執行過程:

select * from t1 where exists ( select * from t2 where t2.col1 = t1.col1 )

相當於:

for x in ( select * from t1 )
loop
if ( exists ( select * from t2 where t2.col1 = x.col1 )
then
OUTPUT THE RECORD in x
end if
end loop

轉載註明出處:http://x- spirit.iteye.com/、http: //www.blogjava.net/zhangwei217245/
也就是說,EXISTS語句實際上是通過循環外部查詢的結果集,來過濾出符合子查詢標準的結果集。於是外部查詢的結果集數量對該語句執行性能影響最大,故 如果外部查詢的結果集數量龐大,用EXISTS語句的性能也不一定就會好很多。
轉載註明出處:http://x- spirit.iteye.com/、http: //www.blogjava.net/zhangwei217245/
當然,有人說NOT IN是對外部查詢和子查詢都做了全表掃描,如果有索引的話,還用不上索引,但是NOT EXISTS是做連接查詢,所以,如果連接查詢的兩列都做了索引,性能會有一定的提升。
當然至於實際的查詢效率,我想還是具體情況具體分析吧。


那麽我們不妨來分析一下語句2為什麽能夠的到正確的結果吧:

語句2是這樣的:

select * from table1 A where not exists ( SELECT B.col1 FROM table2 B where B.col1 = A.col1)


實際上是這樣的執行過程:

for x in ( select * from table1 A )
loop
if (not exists ( select * from table2 B where B.col1 = x.col1 )
then
OUTPUT THE RECORD in x
end if
end loop

由於表A中不包含NULL的記錄,所以,遍歷完表A,也只能挑出表A中獨有的記錄。

這就是為什麽語句2 能夠完成語句3 的任務的原因。

但如果表A中存在NULL記錄而表B中不存在呢?
答案:A表中的NULL也會被查出來。因為select * from table2 B where B.col1 = NULL不返回結果,故
not exists ( select * from table2 B where B.col1 = x.col1 )的值為真。

Oracle not in查不到應有的結果(NULL、IN、EXISTS詳解)