SQL的溫柔陷阱:三值邏輯與NULL
點(diǎn)擊關(guān)注上方“SQL數(shù)據(jù)庫開發(fā)”,
設(shè)為“置頂或星標(biāo)”,第一時(shí)間送達(dá)干貨
什么是NULL
兩種 NULL

為什么必須寫成“IS NULL”,而不是“= NULL”
DROP?TABLE?IF?EXISTS?t_sample_null;
CREATE?TABLE?t_sample_null (
????id?INT(11) unsigned?NOT?NULL?AUTO_INCREMENT COMMENT?'自增主鍵',
????name?VARCHAR(50) NOT?NULL?COMMENT?'名稱',
????remark VARCHAR(500) COMMENT?'備注',
????primary key(id)
) COMMENT?'NULL樣例';
INSERT?INTO?t_sample_null(name, remark)
VALUES('zhangsan', '張三'),('李四', NULL);-- SQL 不報(bào)錯(cuò),但查不出結(jié)果
SELECT?* FROM?t_sample_null WHERE?remark = NULL;
三值邏輯
--?這個(gè)是明確的邏輯值的比較
unknown?=?unknown?→?true
--?這個(gè)相當(dāng)于NULL?=?NULL
UNKNOWN?=?UNKNOWN?→?unknown三值邏輯的邏輯值表



OR 的情況:true > unknown > false
--?假設(shè)?a?=?2,?b?=?5,?c?=?NULL,下列表達(dá)式的邏輯值如下
a?AND?b?>?c??→?unknown
a?>?b?OR?b?a?OR?b?true
NOT?(b?<>?c)?????→?unknown“IS NULL” 而非 “= NULL”
--?以下的式子都會(huì)被判為?unknown
=?NULL
>?NULL
NULL
<>?NULL
NULL?=?NULL溫柔的陷阱
比較謂詞和 NULL
排中律指同一個(gè)思維過程中,兩個(gè)相互矛盾的思想不能同假,必有一真,即“要么A要么非A” 假設(shè)我們有學(xué)生表:t_student
DROP?TABLE?IF?EXISTS?t_student;
CREATE?TABLE?t_student (
????id?INT(11) unsigned?NOT?NULL?AUTO_INCREMENT COMMENT?'自增主鍵',
????name?VARCHAR(50) NOT?NULL?COMMENT?'名稱',
????age INT(3) COMMENT?'年齡',
????remark VARCHAR(500) NOT?NULL?DEFAULT?''?COMMENT?'備注',
????primary key(id)
) COMMENT?'學(xué)生信息';
INSERT?INTO?t_student(name, age)
VALUE('zhangsan', 25),('wangwu', 60),('bruce', 32),('yzb', NULL),('boss', 18);
SELECT?* FROM?t_student;SELECT?*?FROM?t_student
WHERE?age?=?20?OR?age?<>?20;咋一看,這不就是查詢表中全部記錄嗎?我們來看下實(shí)際結(jié)果

-- 1. 約翰年齡是 NULL (未知的 NULL !)
SELECT?*
FROM?t_student
WHERE?age = NULL
OR?age <> NULL;
-- 2. 對(duì) NULL 使用比較謂詞后,結(jié)果為unknown
SELECT?*
FROM?t_student
WHERE?unknown
OR?unknown;
-- 3.unknown OR unknown 的結(jié)果是unknown (參考三值邏輯的邏輯值表)
SELECT?*
FROM?t_student
WHERE?unknown;--?添加 3 個(gè)條件:年齡是20?歲,或者不是20?歲,或者年齡未知
SELECT?*?FROM?t_student
WHERE?age?=?20?
????OR?age?<>?20
????OR?age?IS?NULL;CASE?col_1
????WHEN?=?1?THEN?'o'
????WHEN?NULL?THEN?'x'
END這個(gè) CASE 表達(dá)式一定不會(huì)返回 ×。這是因?yàn)椋诙€(gè) WHEN 子句是 col_1 = NULL 的縮寫形式。正如我們所知,這個(gè)式子的邏輯值永遠(yuǎn)是 unknown ,而且 CASE 表達(dá)式的判斷方法與 WHERE 子句一樣,只認(rèn)可邏輯值為 true 的條件。正確的寫法是像下面這樣使用搜索 CASE 表達(dá)式
CASE?WHEN?col_1?=?1?THEN?'o'
????WHEN?col_1?IS?NULL?THEN?'x'
ENDNOT IN 和 NOT EXISTS 不是等價(jià)的
DROP TABLE IF?EXISTS t_student_A;
CREATE TABLE t_student_A (
????id INT(11) unsigned NOT NULL?AUTO_INCREMENT COMMENT '自增主鍵',
????name VARCHAR(50) NOT NULL?COMMENT '名稱',
????age INT(3) COMMENT '年齡',
????city VARCHAR(50) NOT NULL?COMMENT '城市',
????remark VARCHAR(500) NOT NULL?DEFAULT?''?COMMENT '備注',
????primary key(id)
) COMMENT '學(xué)生信息';
INSERT INTO t_student_A(name, age, city)
VALUE
('zhangsan', 25,'深圳市'),('wangwu', 60, '廣州市'),
('bruce', 32, '北京市'),('yzb', NULL, '深圳市'),
('boss', 43, '深圳市');
DROP TABLE IF?EXISTS t_student_B;
CREATE TABLE t_student_B (
????id INT(11) unsigned NOT NULL?AUTO_INCREMENT COMMENT '自增主鍵',
????name VARCHAR(50) NOT NULL?COMMENT '名稱',
????age INT(3) COMMENT '年齡',
????city VARCHAR(50) NOT NULL?COMMENT '城市',
????remark VARCHAR(500) NOT NULL?DEFAULT?''?COMMENT '備注',
????primary key(id)
) COMMENT '學(xué)生信息';
INSERT INTO t_student_B(name, age, city)
VALUE
('馬化騰', 45, '深圳市'),('馬三', 25, '深圳市'),
('馬云', 43, '杭州市'),('李彥宏', 41, '深圳市'),
('年輕人', 25, '深圳市');
?* FROM t_student_B;--?查詢與?A??班住在深圳的學(xué)生年齡不同的?B?班學(xué)生??
SELECT?*?FROM?t_student_B
WHERE?age?NOT?IN?(
????SELECT?age?FROM?t_student_A?
????WHERE?city?=?'深圳市'
);我們來看下執(zhí)行結(jié)果

-- 1. 執(zhí)行子查詢,獲取年齡列表
SELECT?* FROM?t_student
WHERE?age NOT?IN(43, NULL, 25);
-- 2. 用NOT 和IN 等價(jià)改寫NOT IN
SELECT?* FROM?t_student
WHERE?NOT?age IN?(43, NULL, 25);
-- 3. 用OR 等價(jià)改寫謂詞IN
SELECT?* FROM?t_student
WHERE?NOT?( (age = 43) OR?(age = NULL) OR?(age = 25) );
-- 4. 使用德· 摩根定律等價(jià)改寫
SELECT?* FROM?t_student
WHERE?NOT?(age = 43) AND?NOT(age = NULL) AND?NOT?(age = 25);
-- 5. 用<> 等價(jià)改寫 NOT 和 =
SELECT?* FROM?t_student
WHERE?(age <> 43) AND?(age <> NULL) AND?(age <> 25);
-- 6. 對(duì)NULL 使用<> 后,結(jié)果為 unknown
SELECT?* FROM?t_student
WHERE?(age <> 43) AND?unknown?AND?(age <> 25);
-- 7.如果 AND 運(yùn)算里包含 unknown,則結(jié)果不為true(參考三值邏輯的邏輯值表)
SELECT?* FROM?t_student
WHERE?false?或 unknown;--?正確的SQL 語句:馬化騰和李彥宏將被查詢到
SELECT?*?FROM?t_student_B?B
WHERE?NOT?EXISTS?(?
????SELECT?*?FROM?t_student_A?A
????WHERE?B.age?=?A.age
????AND?A.city?=?'深圳市'?
);執(zhí)行結(jié)果如下

同樣地,我們?cè)賮硪徊揭徊降乜纯催@段 SQL 是如何處理年齡為 NULL 的行的
-- 1. 在子查詢里和 NULL 進(jìn)行比較運(yùn)算,此時(shí) A.age 是 NULL
SELECT?* FROM?t_student_B B
WHERE?NOT?EXISTS?(
????SELECT?* FROM?t_student_A A
????WHERE?B.age = NULL
????AND?A.city = '深圳市'?
);
-- 2. 對(duì)NULL 使用“=”后,結(jié)果為 unknown
SELECT?* FROM?t_student_B B
WHERE?NOT?EXISTS?(
????SELECT?* FROM?t_student_A A
????WHERE?unknown
????AND?A.city = '深圳市'?
);
-- 3. 如果AND 運(yùn)算里包含 unknown,結(jié)果不會(huì)是true
SELECT?* FROM?t_student_B B
WHERE?NOT?EXISTS?(
????SELECT?* FROM?t_student_A A
????WHERE?false?或 unknown
);
-- 4. 子查詢沒有返回結(jié)果,因此相反地,NOT EXISTS 為 true
SELECT?* FROM?t_student_B B
WHERE?true;總結(jié)
作者:youzhibing2904?
https://www.cnblogs.com/youzhibing/p/11337745.html
——End——
后臺(tái)回復(fù)關(guān)鍵字:1024,獲取一份精心整理的技術(shù)干貨 后臺(tái)回復(fù)關(guān)鍵字:進(jìn)群,帶你進(jìn)入高手如云的交流群。 推薦閱讀
快手公司廁所裝坑位計(jì)時(shí)器,網(wǎng)友:再也不能帶薪拉屎了! 如何優(yōu)雅地給妹子優(yōu)化電腦? 最全MySQL面試題集合 有了這 4 款腳本工具,老板再也不怕我寫爛SQL了
這是一個(gè)能學(xué)到技術(shù)的公眾號(hào),歡迎關(guān)注
點(diǎn)擊「閱讀原文」了解SQL訓(xùn)練營(yíng)
評(píng)論
圖片
表情
