一文詳解SQL?中的三值邏輯
1. 前言
大多數(shù)編程語(yǔ)言都是基于二值邏輯的,即邏輯真值只有真和假兩個(gè)。而 SQL 語(yǔ)言則采用一種特別的邏輯體系——三值邏輯,即邏輯真值除了真和假,而 SQL 語(yǔ)言里,除此之外還有第三個(gè)值 unknown
,因此這種邏輯體系被稱為三值邏輯(three-valued-logic)。
2. 兩種 Null
- 表示未知 (unknown):“不知道戴墨鏡的人眼睛是什么顏色”這種情況為例,這個(gè)人的眼睛肯定是有顏色的,但是如果他不摘掉眼鏡,別人就不知道他的眼睛是什么顏色。這就叫作未知。
- 表示不適用 (not applicable 或 inapplicable):“不知道冰箱的眼睛是什么顏色”則屬于“不適用”。因?yàn)楸涓揪蜎](méi)有眼睛,所以“眼睛的顏色”這一屬性并不適用于冰箱。
“冰箱的眼睛的顏色”這種說(shuō)法和“圓的體積”“男性的分娩次數(shù)”一樣,都是沒(méi)有意義的。平時(shí),我們習(xí)慣了說(shuō)“不知道”,但是“不知道”也分很多種。“不適用”這種情況下的 NULL ,在語(yǔ)義上更接近于“無(wú)意義”,而不是“不確定”。
總結(jié):“未知”指的是“雖然現(xiàn)在不知道,但加上某些條件后就可以知道”;而“不適用”指的是“沒(méi)有辦法知道”。
3. 為什么是 is Null 而不是 = Null ?
“我們先從定義一個(gè)表示‘雖然丟失了,但卻適用的值’的標(biāo)記開(kāi)始。我們把它叫作 A-Mark。這個(gè)標(biāo)記在關(guān)系數(shù)據(jù)庫(kù)里既不被當(dāng)作值(value),也不被當(dāng)作變量 (variable)。”(E.F. Codd,The Relational Model for Database Management :Version 2 , P.173) “關(guān)于 NULL 的很重要的一件事情是,NULL 并不是值。”(C.J. Date, An Intruction To Database System (6th edition ), P.619)
對(duì) NULL 使用比較謂詞后得到的結(jié)果總是 unknown 。而查詢結(jié)果只會(huì)包含 WHERE 子句里的判斷結(jié)果為 true 的行,不會(huì)包含判斷結(jié)果為 false 和 unknown 的行。不只是等號(hào),對(duì) NULL 使用其他比較謂詞,結(jié)果也都是一樣的。
-- 以下的式子都會(huì)被判為 unknown 1 = NULL 2 > NULL 3 < NULL 4 <> NULL NULL = NULL
那么,為什么對(duì) NULL 使用比較謂詞后得到的結(jié)果永遠(yuǎn)不可能為真呢?這是因?yàn)椋琋ULL 既不是值也不是變量。NULL 只是一個(gè)表示“沒(méi)有值”的標(biāo)記,而比較謂詞只適用于值。因此,對(duì)并非值的 NULL 使用比較謂詞本來(lái)就是沒(méi)有意義的。(Null只是一個(gè)作為區(qū)分的標(biāo)記,并不是一個(gè)值)
“列的值為 NULL ”“NULL 值”這樣的說(shuō)法本身就是錯(cuò)誤的。因?yàn)?NULL 不是值,所以不在定義域(domain)中。相反,如果有人認(rèn)為 NULL 是值,那么請(qǐng)區(qū)分一下:它是什么類型的值?關(guān)系數(shù)據(jù)庫(kù)中存在的值必然屬于某種類型,比如字符型或數(shù)值型等。所以,假如 NULL 是值,那么它就必須屬于某種類型。( SQL 里的 NULL 和其他編程語(yǔ)言里的 NULL 是完全不同的東西)
4. 第三個(gè)真值 “unknown”
因關(guān)系數(shù)據(jù)庫(kù)采用了 NULL 而被引入了 “第三個(gè)真值”。這里有一點(diǎn)需要注意:真值 unknown 和作為 NULL 的一種的 UNKNOWN (未知)是不同的東西。前者是明確的布爾型的真值,后者既不是值也不是變量。(下文使用 unknown 表示 真值,UNKNOWN 表示 代表Null的一個(gè) 標(biāo)記)
舉個(gè)栗子: unknown = unknown
判定為 true
而 UNKNOWN = UNKNOWN
( 也就是 Null = Null
) 判定為 unknown
5. 包含三值邏輯的真值表
當(dāng)兩個(gè)值進(jìn)行邏輯判斷的時(shí)候的優(yōu)先級(jí),優(yōu)先級(jí)高的真值會(huì)決定計(jì)算結(jié)果:
- AND 的情況: false > unknown > true
- OR 的情況: true > unknown > false
舉個(gè)栗子: true AND unknown ,因?yàn)?unknown 的優(yōu)先級(jí)更高,所以結(jié)果是 unknown 。而 true OR unknown 的話,因?yàn)?true 優(yōu)先級(jí)更高,所以結(jié)果是 true 。
6. “排中律” 不再成立
“把命題和它的否命題通過(guò)‘或者’連接而成的命題全都是真命題” 這個(gè)命題在二值邏輯中被稱為排中律(Law of Excluded Middle)。顧名思義,排中律就是指不認(rèn)可中間狀態(tài),對(duì)命題真?zhèn)蔚呐卸ê诎追置?,是古典邏輯學(xué)的重要原理。
舉個(gè)栗子:現(xiàn)實(shí)生活中 一個(gè)學(xué)生 是20歲 或者 不是20歲,不會(huì)有第三種情況。
但是在SQL中并不是這個(gè)樣:
-- 查詢年齡是20 歲或者不是20 歲的學(xué)生 SELECT * FROM Students WHERE age = 20 OR age <> 20;
在現(xiàn)實(shí)生活中,上面的查詢條件應(yīng)該包含所有的學(xué)生,但是這里的執(zhí)行結(jié)果并不會(huì)查詢到約翰。
--- 當(dāng)查詢到約翰哪一行時(shí)的判定 WHERE Null = 20 OR Null <> 20 --- 根據(jù)上文的描述,該條件會(huì)轉(zhuǎn)換為 WHERE unknown OR unknown 等同于 WHERE unknown
若要查到所有學(xué)生需要再加上一個(gè)條件:OR age IS NULL
7. CASE 表達(dá)式和 NULL
CASE col_1 WHEN 1 THEN '○' WHEN NULL THEN '×' END
上面的這個(gè)CASE表達(dá)式會(huì)在 col_1 為 1 時(shí)返回 ○
、為 NULL 時(shí)返回 ×
嗎?顯然始終不會(huì)返回 x
因?yàn)榈诙€(gè) WHEN 子句是 col_1 = NULL 的縮寫(xiě)形式,根據(jù)上文的描述 col_1 = NULL 始終會(huì)返回 unknown 而 CASE 表達(dá)式的判斷方法與 WHERE 子句一樣,只認(rèn)可真值為 true 的條件,所以 x
并不會(huì)出現(xiàn)。下面才是正確的寫(xiě)法:
CASE WHEN col_1 = 1 THEN '○' WHEN col_1 IS NULL THEN '×' END
8. NOT IN 和 NOT EXISTS 不是等價(jià)的
在對(duì) SQL 語(yǔ)句進(jìn)行性能優(yōu)化時(shí),經(jīng)常用到的一個(gè)技巧是將 IN 改寫(xiě)成EXISTS 。這是等價(jià)改寫(xiě),并沒(méi)有什么問(wèn)題。問(wèn)題在于,將 NOT IN 改寫(xiě)成 NOT EXISTS 時(shí),結(jié)果未必一樣。
查詢 “與 B 班住在東京的學(xué)生年齡不同的 A 班學(xué)生” 。也就是說(shuō),希望查詢到的是拉里和伯杰。
-- 查詢與 B 班住在東京的學(xué)生年齡不同的 A 班學(xué)生的 SQL 語(yǔ)句 SELECT * FROM Class_A WHERE age NOT IN ( SELECT age FROM Class_B WHERE city = '東京' );
這條 SQL 語(yǔ)句真的能正確地查詢到這兩名學(xué)生嗎?遺憾的是不能。結(jié)果是空,查詢不到任何數(shù)據(jù)。根據(jù)前文所說(shuō)的規(guī)則推導(dǎo)一下吧:
--1. 執(zhí)行子查詢,獲取年齡列表 SELECT * FROM Class_A WHERE age NOT IN (22, 23, NULL); --2. 用 NOT 和 IN 等價(jià)改寫(xiě) NOT IN SELECT * FROM Class_A WHERE NOT age IN (22, 23, NULL); --3. 用 OR 等價(jià)改寫(xiě)謂詞 IN SELECT * FROM Class_A WHERE NOT ( (age = 22) OR (age = 23) OR (age = NULL) ); --4. 使用德 · 摩根定律等價(jià)改寫(xiě) SELECT * FROM Class_A WHERE NOT (age = 22) AND NOT(age = 23) AND NOT (age = NULL); --5. 用<> 等價(jià)改寫(xiě) NOT 和 = SELECT * FROM Class_A WHERE (age <> 22) AND (age <> 23) AND (age <> NULL); --6. 對(duì)NULL 使用<> 后,結(jié)果為unknown SELECT * FROM Class_A WHERE (age <> 22) AND (age <> 23) AND unknown; --7.如果AND 運(yùn)算里包含unknown,則結(jié)果不為true SELECT * FROM Class_A WHERE false 或 unknown;
所以 上述查詢語(yǔ)句 查詢不到任何數(shù)據(jù)。為了得到正確的結(jié)果,需要使用 EXISTS 謂詞。
-- 正確的SQL 語(yǔ)句:拉里和伯杰將被查詢到 SELECT * FROM Class_A A WHERE NOT EXISTS ( SELECT * FROM Class_B B WHERE A.age = B.age AND B.city = '東京' ); --1. 在子查詢里和NULL 進(jìn)行比較運(yùn)算 SELECT * FROM Class_A A WHERE NOT EXISTS ( SELECT * FROM Class_B B WHERE A.age = NULL AND B.city = '東京' ); --2. 對(duì)NULL 使用“=”后,結(jié)果為 unknown SELECT * FROM Class_A A WHERE NOT EXISTS ( SELECT * FROM Class_B B WHERE unknown AND B.city = '東京' ); --3. 如果AND 運(yùn)算里包含unknown,結(jié)果不會(huì)是true SELECT * FROM Class_A A WHERE NOT EXISTS ( SELECT * FROM Class_B B WHERE false 或 unknown); --4. 子查詢沒(méi)有返回結(jié)果,因此相反地,NOT EXISTS 為true SELECT * FROM Class_A A WHERE true;
9. 限定謂詞和 NULL
-- 查詢比 B 班住在東京的所有學(xué)生年齡都小的A 班學(xué)生 這里會(huì)正常返回 拉里 SELECT * FROM Class_A WHERE age < ALL ( SELECT age FROM Class_B WHERE city = '東京' );
如果山田年齡不詳,就會(huì)有問(wèn)題了。
--1. 執(zhí)行子查詢獲取年齡列表 SELECT * FROM Class_A WHERE age < ALL ( 22, 23, NULL); --2. 將ALL 謂詞等價(jià)改寫(xiě)為AND SELECT * FROM Class_A WHERE (age < 22) AND (age < 23) AND (age < NULL); --3. 對(duì)NULL 使用“<”后,結(jié)果變?yōu)?unknown SELECT * FROM Class_A WHERE (age < 22) AND (age < 23) AND unknown; --4. 如果AND 運(yùn)算里包含unknown,則結(jié)果不為true SELECT * FROM Class_A WHERE false 或 unknown;
10. 限定謂詞和極值函數(shù)不是等價(jià)的
將 9 中的表 Class_B 中 山田的年齡改為Null,執(zhí)行下面的查詢
-- 查詢比B 班住在東京的年齡最小的學(xué)生還要小的A 班學(xué)生 SELECT * FROM Class_A WHERE age < ( SELECT MIN(age) FROM Class_B WHERE city = '東京' );
這里仍能正確查詢出拉里和伯杰,這是因?yàn)?,極值函數(shù)在統(tǒng)計(jì)時(shí)會(huì)把為 NULL 的數(shù)據(jù)排除掉。使用極值函數(shù)能使 Class_B 這張表里看起來(lái)就像不存在 NULL 一樣。
區(qū)分含義:
- ALL 謂詞:他的年齡比在東京住的所有學(xué)生都小 Q1
- 極值函數(shù):他的年齡比在東京住的年齡最小的學(xué)生還要小 Q2
Q1 和 Q2 不等價(jià)的情況:
- 表里存在 NULL 時(shí)它們是不等價(jià)的
- 謂詞(或者函數(shù))的輸入為空集的情況
這里說(shuō)明一下情況2:B 班里沒(méi)有學(xué)生住在東京。這時(shí),使用 ALL 謂詞的SQL 語(yǔ)句會(huì)查詢到 A 班的所有學(xué)生。然而,用極值函數(shù)查詢時(shí)一行數(shù)據(jù)都查詢不到。這是因?yàn)?,極值函數(shù)在輸入為空表(空集)時(shí)會(huì)返回 NULL 。
--1. 極值函數(shù)返回NULL SELECT * FROM Class_A WHERE age < NULL; --2. 對(duì)NULL 使用“<”后結(jié)果為 unknown SELECT * FROM Class_A WHERE unknown;
11. 聚合函數(shù)和 Null
實(shí)際上,當(dāng)輸入為空表時(shí)返回 NULL 的不只是極值函數(shù),COUNT 以外的聚合函數(shù)也是如此。
-- 查詢比住在東京的學(xué)生的平均年齡還要小的A 班學(xué)生的SQL 語(yǔ)句? SELECT * FROM Class_A WHERE age < ( SELECT AVG(age) FROM Class_B WHERE city = '東京' );
沒(méi)有住在東京的學(xué)生時(shí),AVG 函數(shù)返回 NULL 。因此,外側(cè)的 WHERE 子句永遠(yuǎn)是 unknown ,也就查詢不到行。
到此這篇關(guān)于一文詳解SQL 中的三值邏輯的文章就介紹到這了,更多相關(guān)SQL三值邏輯內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL中replace into與replace區(qū)別詳解
本文主要介紹了MySQL中replace into與replace區(qū)別詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08Mysql5.7中JSON操作函數(shù)使用說(shuō)明
本文給大家分享的是在mysql5.7中操作json的函數(shù)的使用方法以及相關(guān)示例,非常的實(shí)用,有需要的小伙伴可以參考下2017-07-07mysql數(shù)據(jù)庫(kù)備份命令分享(mysql壓縮數(shù)據(jù)庫(kù)備份)
這篇文章主要介紹了mysql數(shù)據(jù)庫(kù)備份常用語(yǔ)句,包括數(shù)據(jù)庫(kù)壓縮備份、備份多個(gè)MySQL數(shù)據(jù)庫(kù)、備份多個(gè)MySQL數(shù)據(jù)庫(kù)、將數(shù)據(jù)庫(kù)轉(zhuǎn)移到新服務(wù)器等語(yǔ)句2014-01-01win10下安裝兩個(gè)MySQL5.6.35數(shù)據(jù)庫(kù)
這篇文章主要為大家詳細(xì)介紹了win10下兩個(gè)MySQL5.6.35數(shù)據(jù)庫(kù)安裝教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05MySql 錯(cuò)誤Incorrect string value for column
能使用中文進(jìn)行搜索,但是insert into 中文是可以的。我的數(shù)據(jù)庫(kù)和數(shù)據(jù)表中所有的charset都是設(shè)置的utf8。2010-12-12在MySQL中創(chuàng)建帶有IN和OUT參數(shù)的存儲(chǔ)過(guò)程的方法
這篇文章主要介紹了在MySQL中創(chuàng)建帶有IN和OUT參數(shù)的存儲(chǔ)過(guò)程的方法,在一定程度上簡(jiǎn)化了操作,需要的朋友可以參考下2015-06-06