三值逻辑(Three Value Logic)
在关系型数据库中,由于NULL值的存在,导致逻辑表达式存在三种值:TRUE/FALSE/UNKNOW。
SELECT '999'=NULL AS C1, '999'='111' AS C2, '999'='999' AS C3; +------+----+----+ | C1 | C2 | C3 | +------+----+----+ | NULL | 0 | 1 | +------+----+----+ 1 row in set (0.00 sec)
NULL值表示不确定或未知的值,因此NULL值是否等于或不等于某个特定值,只能使用IS NULL或IS NOT NULL来判断。
NULL值处理
在大部分场景中,NULL值不等于任何值包括NULL值,但在下列场景中设定NULL值相等:
1、对于唯一索引,仅允许唯一索引列存在一个NULL值。 2、对于GROUP BY和DISTINCT操作,所有NULL值当做同一组处理。 3、对于ORDER BY操作,所有NULL值被当做"最小值"排序在一起。
UNKNOW值处理
对于逻辑表达式计算后得到的UNKNOW值,在不同场景中处理方式不同:
1、在WHERE/ON/HAVING三类筛选操作中,UNKNOW值被当做FALSE处理。 2、在CHECK约束中,UNKNOW值被当做TRUE处理,CHECK约束仅对CHCK表达式为FALSE的记录返回错误。
UNKNOW值踩坑
很多生产案例是由于忽略UNKNOW值或对UNKNOW值处理不当导致,研发人员或DBA经常将逻辑表达式结果按照"非黑即白"(TURE OR FALSE)的结果去处理。
场景1、等值判断和不等值判断随意"转换",如:
表TB1表结构为(ID INT PRIMARY KEY,C1 VARCHAR(20))
A、查询满足C1等于'ABC'的记录,使用SELECT * FROM TB1 WHERE C1='ABC'
B、查询满足C1不等于'ABC'的记录,使用SELECT * FROM TB1 WHERE C1<>'ABC'
C、查询满足C1不等于'ABC'的记录,使用SELECT * FROM TB1 WHERE NOT(C1='ABC')
D、查询满足C1不等于'ABC'的记录,使用SELECT * FROM TB1 WHERE ID NOT IN (SELECT ID FROM TB1 WHERE C1='ABC')
如果表TB1上有C1列为NULL的记录,则上面操作B和操作C返回相同的查询结果,但操作B和操作D返回不同的查询结果,那么那个操作才真正满足业务需求呢?
场景2、IN子查询和NOT IN子查询随意“切换”, 如:
mysql.user表存放用户账号信息 A、使用 select host,user from mysql.user where user not in ('root')可以查出所有user不等于NULL且不等于root的账号。 A、使用select host,user from mysql.user where user not in ('root',NULL);不能查出所有user不等于ROOT和不等于NULL的用户,因为NOT IN条件中包含NULL,所有值包括NULL值在NOT IN(NULL)操作时,都会返回UNKNOW,因此该查询会返回空集。