開發隨筆 NOTINvsNOTEXISTS

2022-03-23 04:38:34 字數 3924 閱讀 3974

之前在論壇中見到乙個針對in/exists的討論,原帖懶得找了,這裡介紹一下最近的學習小結:

not in和not eixts在對允許為null的列查詢時會有一定的風險。特別是not in,如果子查詢包含了最少乙個null,會出現非預期的結果。下面做乙個演示。

if object_id('shipmentitems', 'u') is not null

drop table dbo.shipmentitems;

go create table dbo.shipmentitems

( shipmentbarcode varchar(30) not null ,

description varchar(100) null ,

barcode varchar(30) not null

); go insert into dbo.shipmentitems

( shipmentbarcode ,

barcode ,

description

) select '123456' ,

'1010203' ,

'some cool widget'

union all

select '123654' ,

'1010203' ,

'some cool widget'

union all

select '123654' ,

'1010204' ,

'some cool stuff for some gadget';

go -- retrieve all the items from shipment 123654

-- that are not shipped in shipment 123456

select barcode

from dbo.shipmentitems

where shipmentbarcode = '123654'

and barcode not in ( select barcode

from dbo.shipmentitems

where shipmentbarcode = '123456' );

/* barcode

------------------------------

1010204

*/可以看出得到了期待結果。下面看看修改表結構,允許列為null的情況:

alter table dbo.shipmentitems

alter column barcode varchar(30) null;

insert into dbo.shipmentitems

( shipmentbarcode ,

barcode ,

description

) select '123456' ,

null ,

'users manual for some gadget';

go select barcode

from dbo.shipmentitems

where shipmentbarcode = '123654'

and barcode not in ( select barcode

from dbo.shipmentitems

where shipmentbarcode = '123456' );

/* barcode

------------------------------

*/很多人會覺得這是乙個bug,有時候能查出資料,有時候卻不能。但是實際上不是bug,當not in子句返回最少乙個null時,查詢會返回空,下面的語句能更好地說明這個想法:

select case when 1 not in ( 2, 3 ) then 'true'

else 'unknown or false'

end ,

case when 1 not in ( 2, 3, null ) then 'true'

else 'unknown or false'

end;

/* ---- ----------------

true unknown or false

*/實際上,由於in的本質是or操作,所以: 

select case when 1 in ( 1, 2, null ) then 'true'

else 'unknown or false'

end中,1 in 1,也就是為true,所以返回true,這個語句的邏輯實際上是:

select case when ( 1 = 1 )

or ( 1 = 2 )

or ( 1 = null ) then 'true'

else 'unknown or false'

end ;當使用not in 時,如下面的語句:

select case when 1 not in ( 1, 2, null ) then 'true'

else 'unknown or false'

end ;會轉變成:

select case when not ( ( 1 = 1 )

or ( 1 = 2 )

or ( 1 = null )

) then 'true'

else 'unknown or false' end ;根據離散數學的概念,可以轉換為:

select case when ( ( 1 <> 1 )

and ( 1 <> 2 )

and ( 1 <> null )

) then 'true'

else 'unknown or false'

end ;謂詞有短路特性,即在and條件中,只要有乙個條件為false,整個條件都為false,而1<>1是為false,所以後面的也不需要判斷了,直接返回else部分。即使是1<>null,根據集合論的特性,null和實際資料的對比總是返回unknown,所以也是為false。如果你非要用not in ,請確保子查詢永遠不會有null返回。或者需要額外處理去除null,比如:

select barcode

from dbo.shipmentitems

where shipmentbarcode = '123654'

and barcode not in ( select barcode

from dbo.shipmentitems

where shipmentbarcode = '123456'

and barcode is not null ) ;還有一種方法就是改寫語句,用not exists來等價替換:

select i.barcode

from dbo.shipmentitems as i

where i.shipmentbarcode = '123654'

and not exists ( select *

from dbo.shipmentitems as i1

where i1.shipmentbarcode = '123456'

and i1.barcode = i.barcode );

/* barcode

------------------------------

1010204

*/另外,基於sarg要求,一般不建議用not in/not exists這種反向掃瞄,避免影響效能。還有乙個選擇使用in/exists的要點,就是多列匹配的問題,在t-sql中,多列同時匹配要用exists,而單列匹配可以用exists/in。可能可以用其他寫法來實現in的多列匹配,但是一般我個人會選擇使用exists來匹配多列。

原文出自:csdn部落格:黃釗吉的部落格

BlueZ開發隨筆

從2010年的一月份到現在藍芽的專案已經開始兩個多月了。除去過年的二十天,我們已經做40多天了。面對完全未知的藍芽,我們一步步摸索,直到今天終於有了一點小成績。記下我此時興奮和探索bluez的感觸,以回憶!剛開始做這個專案,只知道做基於linux下bluez的應用程式的開發,然後再移植到開發板上。當...

Ext開發隨筆

今天在開發乙個專案時,前端用的是ext框架,在開發過程中碰到乙個問題 missing in xml expression。因為本人是用firefox瀏覽器的外掛程式firebug做為除錯,所就碰上這事。如果不用firefox可能永遠碰到著。發現問題咱們就來解決問題。使用firedebug跟蹤了一下返...

開發隨筆(一)

現在回首望去,都是不堪路。想當年自己傻傻呼呼的,遇到問題 不求甚解 到頭來,除了問題解決了,最後什麼都沒明白,現在的經驗告訴我,如果時間還夠寬裕,最好找到問題原因。一直認為,發現問題及解決問題的過程中對自己才是最大的幫助。但是真正出現了問題,有時候卻又是及其的糾結 煩悶,但是問題還是要解決的,不可能...