1. 程式人生 > >SQL陷阱-in與not in不是相反的

SQL陷阱-in與not in不是相反的

轉載自https://blog.csdn.net/h75115200/article/details/76013829

SQL中In與Not In的小陷阱

資料庫中的三值邏輯
在SQL中,邏輯值與其他程式語言不同,其他程式語言往往只有true和false,而在SQL中,還多了一個值UNKNOWN,當與NULL進行比較時會出現這種值,如(1==NULL)結果為UNKNOWN。下面看看維基百科的詳細說明。

資料庫查詢語言SQL實現三值邏輯作為處理NULL欄位內容的一種方式。SQL使用NULL來表示在資料庫中缺失資料。如果一個欄位不包含定義的值,對於SQL這意味著實際的值存在,但是這個值當前沒有記錄在資料庫中。注意缺失的值不同於數值零或零長度字串值;這兩者都表示已知的值。比較任何東西於NULL—即使是另一個NULL—結果是UNKNOWN真值狀態。例如,考慮下列SQL表示式:
City = ‘Paris’
在SQL中,在City欄位中的NULL值表示在理論中導致這個表示式被確認為要麼TRUE(比如City包含’Paris’)要麼FALSE(比如City包含’Philadelphia’)的一個缺失的值。樣例SQL表示式依據如下規則確認:
對於在City欄位中有文字串’Paris’的任何記錄結果為TRUE
對於在City欄位中有NULL的任何記錄結果為UNKNOWN
在所有其他情況結果為FALSE

三值邏輯可能帶來的陷阱
正是因為存在著第三值UNKNOWN,所以容易導致開發者掉入下面的陷阱。

首先假設我們有一個僱員表,僱員有身份證號,姓名,性別,3個屬性,其中只有性別可以為NULL,建表語句如下:

CREATE TABLE `emp` (
  `id` varchar(18) NOT NULL,
  `ename` varchar(20) NOT NULL,
  `esex` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

這時候,我們往表裡寫入一條資料,(445121199001011111,小明,0)
,(445121199001012222,小紅,1),(445121199002021122,小江,NULL)。其中0表示性別為男,1表示性別為女。

那麼接下來,我們用in進行查詢,查詢語句如下:

select * from emp where esex in (0,1);

這條語句的意圖是想查詢性別為男或性別為女的,無性別的將被忽略。很顯然,執行語句的結果集數量為2,即查出了小明和小紅。

我們來看另外一條語句:

select * from emp where esex not in (0,1);

這條語句原本的意圖是想查出性別不是男也不是女的,也就是查出小江的資料。但是結果卻是空集。一條資料也沒有!

陷阱來源
回顧SQL語法,我們知道執行in或not in時,我們是按照返回的布林值的真或假,來決定是否將資料加入結果集。那麼當判斷返回的值為UNKNOWN時,那麼資料必然不會被加入結果集。如果還不明朗,我們可以看看not in的等價關係。

esex not in(0,1)等價於:
esex != ANY(0,1),也等價於
esex != 0 AND esex != 1

注意這裡,esex != 0 AND esex != 1。當esex為NULL時,根據上面的內容,我們知道esex!=0會返回UNKNOWN,整個表示式的返回值也為UNKNOWN。再看看具體資料,小江的esex為NULL,那麼小江的這一條記錄,返回值為UNKNOWN,故不會被加到結果集。

常見陷阱
有了上面的鋪墊,我們在使用In或Not In時,就應該更加的小心謹慎(其他返回值為布林型別的也同理)。尤其是子查詢,下面是常見的例子

SELECT * FROM emp WHERE emp.`esex` NOT IN (SELECT esex FROM emp)

上面的返回集為空集。注意這裡子查詢直接使用了emp表,僅僅為了對應上面的結果。在日常開發中,該子查詢的emp表可能為任意的關聯表,只要該關聯表中,存在有esex為NULL的資料,都會導致最終查詢結果為空集。