SQL總復習三:true、false、unknown 和 null


什么是謂詞?

SQL 的保留字中,有很多都被歸為謂詞一類。例如,“=、<、>”等比較謂詞,以及 BETWEEN、LIKE、IN、IS NULL 等。

謂詞是一種特殊的函數,返回值是真值。前面提到的每個謂詞,返回值都是 truefalse 或者 unknown(一般的謂詞邏輯里沒有unknown,但是 SQL 采用的是三值邏輯,因此具有三種真值)。

image-20211011161718242

1、SQL中的bool類型的值有三種

普通編程語言里的布爾型只有 truefalse 兩個值,這種邏輯體系被稱為二值邏輯。而 SQL 語言里,除此之外還有第三個值 unknown,因此這種邏輯體系被稱為三值邏輯(three-valued logic)。

三個真值之間有下面這樣的優先級順序。

AND 的情況:falseunknowntrue

OR 的情況:trueunknownfalse

優先級高的真值會決定計算結果。例如 true AND unknown,因為unknown的優先級更高,所以結果是 unknown。而true OR unknown的話,因為 true 優先級更高,所以結果是 true

unknown是因關系數據庫采用了 NULL 而被引入的,他不是“未知”的這個意思,而是“無意義”的這個意思。而null是指“未知”的意思。

注意:unknown不能像true或者false一樣,直接在SQL中使用,比如where Tel=unknown

2、null不是值,null與數學運算符一起使用的結果永遠是unknown

為什么對 NULL 使用比較謂詞后得到的結果永遠不可能為真呢?這是因為,NULL 既不是值也不是變量。NULL 只是一個表示“沒有值”的標記,而比較謂詞只適用於值。因此,對並非值的 NULL 使用比較謂詞本來就是沒有意義的。

常聽到的“列的值為 NULL” 、“NULL 值”這樣的說法本身就是錯誤的。因為 NULL 不是值!(如果有人認為 NULL 是值,那么它是什么類型的值?關系數據庫中存在的值必然屬於某種類型,比如字符型或數值型等。所以,假如 NULL 是值,那么它就必須屬於某種類型。)

消除 NULL 的具體方法,這里總結如下。

(1) 首先分析能不能設置默認值。

(2) 僅在無論如何都無法設置默認值時允許使用 NULL

筆者認為,如果遵守這兩條原則,那就足以避免 NULL 帶來的各種問題,使系統開發能夠更加順利地進行。

另外,注意:要想 和 null 比較 只能用 is null 或者 is not null,這樣才會返回true或者false。另外永遠記住一點,null和<,>,=,<>這些放在一起結果永遠是unknown,比如如 2=null,結果肯定是unknown,而unknown在三值邏輯中不是true也不是false,在寫where子句的篩選條件時尤其要注意。舉例來講:

image

我們經常會遇到判斷篩選條件的結果(為true/false/unknown的一種,且SQL只會取返回結果是true的記錄),它們通常是and 或or連接這些單個條件的,如:where age>18 and sex=0或where age<18 or sex =unknown。

請務必牢記

and運算,只要有一邊是unknown,另一邊是false,那結果就是false,其它情況下,只要任意一邊有unknown,結果就是unknown。

or運算,只要一邊是unknown,那么結果永遠就是unknown

not unknown 的結果是 unknown

image-20210913102717107

case與null

當case使用的變量或列的值可能為null時,唯一正確的使用方式如下:

CASE 
	WHEN col_1 = 1 THEN '○'
 	WHEN col_1 IS NULL THEN '×'
END

而不是:

CASE col_1
 WHEN 1 THEN '○'
 WHEN NULL THEN '×'
END

3、NOT IN 和 NOT EXISTS 不是等價的

如果 NOT IN 子查詢中用到的表里被選擇的列中存在 NULL,則 SQL 語句整體的查詢結果永遠是空

EXISTS 謂詞永遠不會返回 unknownEXISTS 只會返回 true 或者 false

因此就有了 INEXISTS 可以互相替換使用,而 NOT INNOT EXISTS 卻不可以互相替換的混亂現象。

4、ALL運算符與null

以下是ALL運算符語法:

scalar_expression comparison_operator ALL ( subquery )

在上面語法中,

  • scalar_expression是任何有效的表達式。
  • comparison_operator是任何有效的比較運算符,包括等於(=),不等於(<>),大於(>),大於或等於(>=),小於(<),小於或等於(<=)。
  • 括號內的子查詢(subquery)是一個SELECT語句,它返回單個列的結果。 此外,返回列的數據類型必須與標量表達式的數據類型相同。

如果all里面的子查詢返回的單列中有null的存在,那么這個all表達式就永遠不會篩選出任何數據,結果肯定為空。

因為ALL 謂詞其實是多個以 AND 連接的邏輯表達式的省略寫法

如果all里面的子查詢返回的單列中有null的存在,比如子查詢結果如下面這個情況,那么具體的分析步驟如下所示。

--1. 執行子查詢獲取年齡列表
SELECT *
  FROM Class_A
 WHERE age < ALL ( 22, 23, NULL );
--2. 將ALL 謂詞等價改寫為AND
SELECT *
  FROM Class_A
 WHERE (age < 22) AND (age < 23) AND (age < NULL);
--3. 對NULL 使用“<”后,結果變為 unknown
SELECT *
  FROM Class_A
 WHERE (age < 22) AND (age < 23) AND unknown;
--4. 如果AND 運算里包含unknown,則結果不為true
SELECT *
  FROM Class_A
 WHERE false 或 unknown;
--5.查詢結果為空

5、極值函數(max, min)、count以外的聚合函數(sum,average)與null

極值函數在輸入為空表(空集)時會返回 NULL。即從一個為空的集合中,選取最大或最小值,會得到null。

因此,建議在使用這些可能返回null的函數的地方,外面套一層isnull函數(SqlServer)來處理

count與null

COUNT 函數的使用方法有 COUNT(*) 和 COUNT( 列名 ) 兩種,它們的區別有兩個:第一個是性能上的區別;第二個是 COUNT(*) 可以用於 NULL, 而 COUNT( 列名 )與其他聚合函數一樣,要先排除掉 NULL 的行再進行統計。

第二個區別也可以這么理解:COUNT(*) 查詢的是所有行的數目,而COUNT( 列名 ) 查詢的則不一定是。


參考圖靈社區的《SQL進價教程》


更新於:2023-4-6


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM