SQL进阶之路03:三值逻辑和NULL

大多数的编程语言都是基于二值逻辑(true,false),而SQL语言采用为三值逻辑(true,false,'unknown'),而这个不确定因素常常导致查询结果并不是理想中的结果。总之存在null那么查询结果就无法保证一定正确。

为什么采用三值逻辑?因为关系数据库中引进了NULL!!!

两种NULL的定义:“未知(unknown)”和“不适用(not applicable,not inapplicable)”;

未知:不知道戴墨镜的人眼睛是什么颜色,颜色一定存在只是并不知道。

不适用:不知道冰箱的眼睛是什么颜色,冰箱根本没有眼睛所以“眼睛颜色”并不适用。

1,为什么在SQL中使用“ = NULL”报错而非要使用“IS NULL” 

解:一旦使用了比较谓词(=,>,<,<>)那么得到的结果总为unknown,而查询结果只包含了true数据行,并不会包含false与unknown数据行。

NULL:既不是值也不是变量,NULL只是一个表示“没有值“的一个标记,而比较谓词只适用于值,因此对于非值的NULL来使用比较谓2词并没有什么意义;

2,unknown

真值unknown和作为NULL的一种unknown(未知)并不是相同的东西,前者为布尔值的真值,后者既不是值也不hi变量,为了区分前者采用小写后者采用大写。

当x=x等式中

x=真值unknown时,unknown=unknow判断为true。

x=UNKNOWN时,UNKNOWN=UNKNOWN判断为null; 

SQL遵循的三值逻辑真值表:

 NOT:比较简单;

AND:false>unknown>true;

OR:true>unknown>false;

优先级高的真值决定计算结果,列如:true AND nuknown:结果为unknown(优先级更高),true OR unknown:结果为:true;

当AND运算中包含unknown,结果肯定不会为true,如果AND运算结果为TURE,则参与的双方都必须为true;

问题:假设a=2,b=5,c=null;

a<b AND b>c : true and unknown   :unknown

a>b or b>c :     false and unknown  :  unknown

a<b or b<c:      ture and nuknown    : true

NOT(b<>c):     not( nuknown)  : unknown

3.实践内容: 

1,比较谓词和NULL(1):排中率不成立

问题描述:假设李四时一个人,那么下面语句是真是假?

李四是20岁,或者不是20岁,二者必居其一!

你觉得这句话正确吗,在现实中是真命题。我们不知道李四是谁,但是知道李四是人,只要是人就有年龄,要么就是20岁要么就不是20岁,不可能还有其他情况。

像这样,“把命题和 它的否命题通过‘或者’连接而成的命题全都是真命题”这个命题在二 值逻辑中被称为排中律(Law of Excluded Middle)。顾名思义,排中 律就是指不认可中间状态,对命题真伪的判定黑白分明,是古典逻辑 学的重要原理。(摘抄)

假设SQL中排中律也成立,那么下面查询应该能选中表里的所有行?

SELECT * FROM USER
WHERE AGE = 20 OR AGE != 20;

SQL世界中排中率并不存在,如果一个表里有NULL值那么就不会将其全部查询出来;

2,比较谓词和NULL:CASE表达式和NULL

SELECT 
CASE PRICE 
	WHEN 50 THEN 'O'
	WHEN NULL THEN 'X' 
END 
from products2

解:CASE表达式一定不会返回X,第二格WHEN子句时price = NULL的缩写,这个式子真值永远是unknown,而且CASE判断方式与WHERE一致。只认可真值为true的条件。

SELECT 
CASE  
	WHEN price = 50 THEN 'O'
	WHEN price IS NULL THEN 'X' 
END 
from products2

3,NOT IN 和 NOT EXISTS不是等价的

性能优化时,经常将IN改写为EXISTS,这是等价改写,但是将NOT IN 改为NOT EXISTS时结果却未必一致;

-- 问题:查找2班住在南京的学生年龄不同于1班的学生!
select * from class1
where age not in( select age from class2 where city='南京') 
-- 注意:当判断存在NULL值的时使用上面SQL无法正常查询,但是无NULL就可以正常查询

--详细查询
SELECT * FROM CLASS1 WHERE AGE NOT IN (22,23,null)
-- 用NOT 和IN 等价改写NOT IN
select * from class1 where not age in (22,23,null)
-- 用OR 等价改写谓词IN
select * from class1 where not( (age = 22) and not (age =23) and not (age =null))
-- 使用德· 摩根定律等价改写
select * from class1 where not (age =22) and not(age=23) and not(age = null)
-- 用<> 等价改写 NOT 和 =
select * from class1 where (age <> 22) AND (age <> 23) AND (age <> NULL);
-- 对NULL 使用<> 后,结果为unknown
select * from class1 where (age <> 22) AND (age <> 23) AND unknown;
-- 如果AND 运算里包含unknown,则结果不为true(参考“理论篇”中的矩阵)
select * from class1 where false or nuknown

 结果用OR 等价改写谓词IN成功查询出结果!(MYSQL)

从这里看出没有一行在WHERE子句判断为true,如果NOT IN 子句查询中用到表中被选择的字段中存在NULL,则SQL查询永远为空!

4,采用EXISTS谓词

-- 查找2班住在南京,但是学生年龄不同的A班学生;
-- 结果为:1班年龄不等于2班住在南京学生的年龄
select * from class1 a 
where not exists (select * from class2 b where a.age = b.age and b.city = '南京' )

 我们详细查看年龄为NULL的数据行如何处理的!

-- 1,子查询里和NULL做比较运算
select * from class1 a where not exists (
		select * from class2 b where a.age = null and  b.city = '南京'
)
-- 2. 对NULL 使用“=”后,结果为 unknown  
select * from class1 a 
where not exists (select * from class2 b where 'unknown' and b.city = '南京' )
-- -3. 如果AND 运算里包含unknown,结果不会是true 
where not exists (select * from class2 b where false or 'unknown' )

EXISTS语句并不会返回unknown,只会返回false和true。因此有了IN和EXISTS可以相互交替使用而NOT EXISTS 和 NOT IN 无法相互替换使用。

 5,限定谓词和NULL

SQL中存在ALL和ANY两个限定谓词,因此ANY和IN是等价的,所以我们不常使用ALL;

ALL可以和比较谓词一起使用,来表达“与所有的XX相等”或者“比所有的XX都大“的意思!

接下来将1班和2班NULL值填充一下,然后使用此表来思考下”查询比2班住在南京的所有学生年龄都小的A班学生“的SQL语句

select * from class1 
where age > all (select age from class2 where city = '南京')

 查询出的结果都是比all(age)大的数据行;

-- 执行子查询结果获取年龄列表  MYSQL(语法报错)
select * from class1 where age < all(22,23,null)
-- 将ALL改写and    无结果
select * from class1 where (age>23) and (age > 22) and (age > null)
--  对NULL 使用“<”后,结果变为 unknown   MYSQL(语法报错)
SELECT * FROM Class1 WHERE (age < 22) AND (age < 23) AND unknown;
--  如果AND 运算里包含unknown,则结果不为true    无结果
SELECT * FROM Class1 WHERE false or 'unknown';

NULL的值导致很多问题,从上面的一些SQL也体会了吧!

6,限定谓词和极值函数不是等价的

使用极值函数来改写SQL

-- 查询比B班住在南京的年龄最小的学生还要小的A班学生
select * from class1 where age <(select min(age) from class2 where city = '南京')

会发现,即使存在NULL值也不会影响SQL的查询结果,因为极值函数会将NULL值排除掉!

但是使用ALL谓词时如果2班没有南京的学生将查询到所有的1班学生!

select * from class1 where age < all(select age from class2 where city = '上海')

如果使用极值函数那么将一个都查询不出来;

select * from class1 where age < (select min(age) from class2 where city = '上海')

极值函数本质上就是 age < null

7,聚合函数和NULL

 实际上,输入为空表时返回NULL的不只是极值函数,COUNT以外的聚合函数也是如此!!

 本节总结:

        1.NULL不是值

        2.因为NULL不是值所以不能使用谓词(>,<,!=)

        3.对NULL使用谓词结果就是unknown

        4.unknown参与逻辑运算时,SQL运算结果会和预想的不一致

        5.解决NULL带来的问题就是NOT NULL 约束

问题总结:

Q:为什么WHERE co l_1 = NULL 会出错?
A:NULL不是值,只是一个“没有值”的标记,需要用 where col_1 is null;

select * from class1 where age = null;  -- 结果为空

select * from class1 where age is null;

Q:排中律在SQL中是否成立?(排中律:一个人年龄要么等于20,要么不等于20)
A:在SQL中,排中律不存在

select * 
    from class1
    where age = 20 
    or age <> 20;     -- 无法查询出年龄为NULL的学生

Q:CASE表达式中,NULL应该怎么写?
A:搜索表达式 + IS NULL

SELECT 
CASE  
    WHEN price = 50 THEN 'O'
    WHEN price IS NULL THEN 'X' 
END 
from products2

NOT IN  和 NOT EXSIST不等价  

Q:查询“与B班住在东京的学生年龄不同的A班学生”,即“拉里”和“伯杰”
A:不能用NOT IN

  • NOT IN:在子查询存在NULL时,查询结果永远为空
  • NOT EXISTS:不受子查询中NULL影响

限定谓语 (ALL、ANY) 和NULL

Q:ANY相当与IN;ALL和比较谓语联用表示“与所有的XX都相等”、“比所有的XX都大”,若遇到NULL
A:若ANY、ALL遇到NULL,查询结果永远为空

极值函数 (MAX、MIN) 和NULL

Q:MAX和比较谓语联用表示“比XXX里最大的更大”,若遇到NULL
A:极值函数只对非NULL起作用

  • 若少量值为NULL,则MAX表示“比XXX剩下非NULL里最大的更大”;
  • 若整个子查询为NULL,则查询结果为空

聚集函数 (COUNT、AVG) 和NULL

了COUNT(*),其他聚集函数与极值函数一样,只对非NULL起作用

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值