天天看點

SQL Server null知多少?

null是什麼?

不知道。我是說,他的意思就是不知道(unknown)。

它和true、false組成謂詞的三個邏輯值,代表“未知”。與true和false相比,null最難以令人捉摸,因為它沒有明确的值,在不同的場景下,它能代表不同的含義。下文以例子的方式給大家分享下null使用的典型場景及對應的用法。

1.check限制與null

之前在SQL ServerCentral.com上看到一個關于check限制的null問題,

作者建立了一個表,在字段orderstatus上設定了check限制,隻能插入指定的value的行,現在插入幾行資料,其中有一行的value為null,最後問最終有幾行可以插入成功。

原文如下:

I want to ensure that the status column for my Orders table only contains specific values. I decide to use this code:

create table Orders
( OrderID int primary key
, OrderTotal MONEY
, OrderStatus VARCHAR(20)
constraint Orders_Status_Code check( OrderStatus in ('ACTIVE', 'INACTIVE', 'TBD'))
);
go      

Now I want to insert data into the table. I run this batch.

insert Orders select 1, 435.43, 'Active'
insert Orders select 2, 554.66, 'InActive'
insert Orders select 3, 129.12, 'Not Active'
insert Orders select 4, 1228.00, NULL      

How many rows are in the table? I am running on a default, SQL Server 2014 instance with US English defaults.

(大家先想想答案,如果沒有把握就找個測試環境試一試)

《T-SQL基礎教程》中關于check限制與null的描述,著者用了一句言簡意赅的口訣“check限制拒絕false,接受true和null”。

在上面的例子中,當orderstatus為‘Avative’和’InActive’時,check限制判斷的結果是true,是以會插入成功,當為'Not Active’判斷的結果為false,插入不成功,最後當為'Null’時,判斷的結果是null,插入成功。

是以,正确答案是3。

2.比較運算與null

null一個特殊性在于它無法比較(和計算)。null與任何值的任何比較(和計算)都等于null。(unique限制除外,在unique限制中,null是相等的,同一個字段不允許出現兩次null)

比如判斷null=null的結果是null,判斷null<>null的結果也還是null。下面我以不等于(<>)為例,示範比較運算對null的判斷。

我先建立一個表,然後插入多行資料,其中有一行orderstatus的值為null,

if object_id(N’Orders’) is not null drop table orders      
create table Orders
( OrderID int primary key
, OrderTotal MONEY
, OrderStatus VARCHAR(20)
);
go      
insert Orders select 1, 435.43, 'Active'
insert Orders select 2, 554.66, 'InActive'
insert Orders select 3, 129.12, 'Not Active'
insert Orders select 4, 1228.00, NULL      

現在我執行了一個where orderstatus<>'Active' 的查詢,

select * from orders where OrderStatus<>'Active'      

大家想想null所在的行會不會在查詢結果裡面。

在上面的例子中,當orderstatus為'InActive' 和'Not Active' 時,where條件判斷的結果為true,但當orderstatus為'null' 時,where OrderStatus<>'Active'等價于where null <>'Active',而null與任何一個值的比較結果還是null,是以where條件判斷的結果為null。

在SQL Server中,where篩選的原則是“接受true,拒絕false和null”(《T-SQL基礎教程》)。是以orderstatus為'InActive' 和'Not Active'的行顯示在結果集總,而orderstatus為null的行不會出現在結果集中。

最終,正确答案是:隻會傳回兩行

SQL Server null知多少?

3.Not in與null和Not exists與null

not in和not exists都可以用來判斷某個對象的存在與否,在大多數場景下兩者可以互相替換,但在遇到null時,因為前者是三值邏輯(true|false|unknow)判斷而後者隻會傳回true或false,是以處理的結果會有很大不同。

為了示範兩者的差別,我們還是沿用上文的表,分别使用not in和not exists執行一個查詢,找出OrderStatus 不為'Active'和'InActive'的行。

if object_id(N’Orders’) is not null drop table orders      
create table Orders
( OrderID int primary key
, OrderTotal MONEY
, OrderStatus VARCHAR(20)
);
go      
insert Orders select 1, 435.43, 'Active'
insert Orders select 2, 554.66, 'InActive'
insert Orders select 3, 129.12, 'Not Active'
insert Orders select 4, 1228.00, NULL      

3.1Not In與null

在下面這個查詢中,where子句中使用not in來過濾資料,where子句的條件是OrderStatus not in ('Active','InActive'),我們期望結果集中包含orderstatus為'Not Active'、'NULL'這兩行的資料。

select * from orders where OrderStatus not in ('Active','InActive')      

這個查詢中,當OrderStatus為null時, 原where子句等價于where null <>'Active' AND  null<>'InActive',這就變成了上文中介紹的比較運算與null的問題。where的判斷結果還是null,是以該行不會出現在結果集中。而當OrderStatus為'Not Active'時,滿足where篩選的為true的條件,會顯示在結果集中。

最終,正确答案是:隻有一行。

SQL Server null知多少?

說明:in與null的關系與此同理。

3.2Not exists與null

現在我們還是期望結果集中包含orderstatus為'Not Active'、'NULL'這兩行的資料,這次用Not exists。

在這個查詢中,子查詢先求出OrderStatus='Active' or  OrderStatus='InActive的行,然後外部查詢用not exists過濾子查詢的結果,将剩下的行顯示在最終結果集中。

SELECT *
FROM orders AS o1
WHERE NOT EXISTS( 
                  SELECT *
                  FROM orders AS o2
                  WHERE o1.OrderStatus = o2.OrderStatus
                    AND ( o2.OrderStatus = 'Active'
                       OR o2.OrderStatus = 'InActive'
                        ));      
SQL Server null知多少?

為了友善了解,我們将子查詢改寫成自表連接配接的方式,

select * from orders as o2 where  o1.OrderStatus=o2.OrderStatus and (o2.OrderStatus='Active' or  o2.OrderStatus='InActive' ))      

改寫成:

SELECT *
FROM orders AS o2
     INNER JOIN orders o1 ON o1.OrderStatus = o2.OrderStatus
                         AND ( o2.OrderStatus = 'Active'
                            OR o2.OrderStatus = 'InActive'
                             );      

傳回的結果集為:

SQL Server null知多少?

然後我們再看外層查詢,

外部查詢期望使用not exists傳回orders表中不包含子查詢結果集的行,也就是說,隻要orders表沒有子查詢結果集中的行就傳回true,否則傳回false(隻有存在和不存在,沒有unknown的說法)。

按照這個邏輯,orderID為3和4的行不在子查詢的結果集中,是以not exists判斷為true,而orderID為1和2的行已包含在子查詢的結果集中,是以not exists判斷為false。最後根據where篩選“接受true,拒絕false和null”的原則,最終隻有orderID為3和4的行顯示在結果集中。

SQL Server null知多少?

說明:exists與null的關系與此同理。

3.3Not  in和Not exists的差別

not in實際上是對一個對象的比較運算,而比較存在true|false|unknow三種邏輯值。

not exsits判斷某個對象存在或者不存在,它隻有這兩種狀态,沒有unknown的說法。是以相比not in而言,not exists隻會有true和false這兩種邏輯值。

總結:

上文介紹了null在不同場景中的含義,考慮到SQL不同的語言元素對null的不同處理方式,平常我們在寫SQL語句的時候應該清晰思考自己編寫的每個查詢對null或三值邏輯的處理,避免出現邏輯錯誤。