SQL的三值逻辑
SQL除了有true和false两个值外,还有第3个值unknown(因为数据库引进了NULL),这种逻辑体系被称为三值逻辑。
1 三值逻辑
为什么必须写成“IS NULL”,而不是“= NULL”?
SELECT * FROM t_table WHERE col_1 = NULL;
这条语句永远查询不出结果,因为对NULL使用比较谓词(=、>或<)后得到的结果总是unknown(NULL既不是值也不是变量,它只是一个表示“没有值”的标记,而比较谓词只适用于值。因此,对并非值的NULL使用比较谓词本来就没有意义的)。
!unknown= unknown
unknown or TRUE = TRUE
unknown or FALSE = unknown
unknown AND FALSE = FALSE
unknown AND TRUE = unknown
1.1 实践
1.1.1 排中律不成立
命题:小李是20岁,或者不是20岁。
在现实世界中,这是一个真命题。这种在二值逻辑中的命题被称为排中律。
SELECT * FROM t_student WHERE age = 20 OR age != 20;
上面这条sql 当遇到age 字段为空的数据时,将获取不到这行。 即 NULL = 20 OR NULL != 20 => unknown
1.1.2 NOT IN 和 NOT EXISTS 不是等价的
SELECT * FROM t_student1 WHERE age NOT IN (SELECT age FROM t_student2);
上面这条sql当t_student2表中存在age为空的数据时,将永远获取不到数据。
age NOT IN (12,NULL) => age != 12 AND age != NULL => unknown 或者FALSE,改成以下sql,将能得到正确结果。
SELECT * FROM t_student1 s1 WHERE NOT EXISTS ( SELECT * FROM t_student2 s2 WHERE s1.age = s2.age);
EXISTS 谓词永远不会返回unknown,只会返回true或者false。
1.1.3 限定谓词和极值函数
SQL 里有ALL和ANY两个限定谓词。ANY 与 IN 是等价的。ALL可以和比较谓词一起使用,表达“与所有的都相等”或“比所有的都大”等。
SELECT * FROM t_student1 WHERE age > ALL (SELECT age FROM t_student2);
上面的sql表示找出t_student1 表中age字段比t_student2表的age字段都大的数据。如果t_student2 表中所有数据的age字段都不为空,那么上面的sql能检索出正确的数据,但是如果t_student2表存在age字段为空的数据,那么这条sql将永远无法检索出数据。
可以用极值函数改进上面的SQL。
SELECT * FROM t_student1 WHERE age > SELECT MAX(age) FROM t_student2;
使用极值函数,当t_student2存在为NULL的数据时,将会忽略掉这条数据。但是,如果t_student2 是个空表,或者改变所有数据的age字段都为NULL时,极值函数将返回NULL。
实际上,当输入为空表时,返回NULL的不止是极值函数,除COUNT以外(如SUM、AVG等)的聚合函数也是如此。
1.1.4 字符串与NULL
在MySQL及大部分数据库中,NULL与字符串的连接,将会返回NULL,例如CONCAT(‘a’,NULL) => NULL。
但是在Oracle中,处理空字符串与NULL的规则和其他数据库不太一样:
1.原则上将空字符作为NULL进行处理。
SELECT * FROM t_student WHERE `name` IS NULL;
这条SQL能检索到字段name为NULL或者为空字符的数据。
2.仅在进行字符串连接时,才将NULL视为空字符。
CONCAT(‘abc’,NULL) => abc
3.字符连接运算时,仅当两个运算对象都是NULL的情况才将它们视为NULL。
CONCAT(NULL,NULL) => NULL