开心一刻
十年前,我:我交女票了,比我大两岁
妈:不行!赶紧分!
八年前,我:我交女票了,比我小两岁,外地的
妈:你就不能让我省点心?
五年前,我:我交女票了,市长的女儿
妈:别人还能看上你?分了吧!
今年,我挺着大肚子踏进家门
妈:闺女啊,你终于开窍了!
谓词
SQL 中的谓词指的是:返回值是逻辑值的函数
我们知道函数的返回值有可能是数字、字符串或者日期等等,但谓词的返回值全部是逻辑值(true/false/unknown
),谓词是一种特殊的函数
此时你们是不是有疑问:逻辑值不是只有 true
和 false
吗,哪来的 unknown
?
那不巧了吗,我正好有说明:神奇的 SQL 之温柔的陷阱 → 为什么是 IS NULL 而非 = NULL ?
里面就讲到了三值逻辑,你们一定要去阅读,方便后续的理解
SQL 中的谓词有很多,如 =、>、<、<> 等,我们来看看 SQL 具体有哪些常用的谓词
比较谓词
创建表与初始化数据
1 | sql复制代码-- 1、表创建并初始化数据 |
相信你们对 =、>、<、<>(!=)等比较运算符都非常熟悉,它们的正式名称就是 比较谓词
,使用示例如下
1 | sql复制代码-- 比较谓词示例 |
LIKE
对于 LIKE
,我相信你们也非常熟悉
当我们想用 SQL 做一些简单的模糊查询时,都会用到 LIKE
谓词,分为 前一致、中一致和后一致,使用示例如下
1 | sql复制代码-- LIKE谓词 |
如果 name
字段上建了索引,那么前一致会利用索引,而中一致、后一致会全表扫描
BETWEEN
当我们想进行范围查询时,往往会用到 BETWEEN
谓词,示例如下
1 | sql复制代码-- BETWEEN谓词 |
BETWEEN
和它之后的第一个 AND
组成一个范围条件
BETWEEN
会包含临界值 15 和 22
BETWEEN
可以 比较谓词 等价替换
1 | sql复制代码SELECT * FROM tbl_student WHERE age BETWEEN 15 AND 22; |
若不想包含临界值,那就需要这么写了
1 | sql复制代码SELECT * FROM tbl_student WHERE age > 15 AND age < 22; |
IS NULL 和 IS NOT NULL
关于 NULL
,不是一言两语能说清的,她的水很深,深的让你又爱又恨!
依旧很巧,我对她已经进行了很深入的研究:神奇的 SQL 之温柔的陷阱 → 为什么是 IS NULL 而非 = NULL ?
你们一定要去仔细观摩,“姿势”
很丰富哟!
IN
有这样一个需求:查询出年龄等于 15、18以及20的学生,我们会用 OR
来查
1 | sql复制代码-- OR |
用 OR
来查没问题,但是有一点不足,如果选取的对象越来越多,SQL 会变得越来越长,阅读性会越来越差,此时我们可以用 IN
来代替
1 | sql复制代码-- IN |
IN
有一种其他谓词没有的使用方法:使用子查询作为其参数
这个在平时项目中也是用的非常多的,例如:查询出影视7班的学生信息
1 | sql复制代码-- IN 可以实现,但不推荐 |
很多情况下,IN
是可以用联表查询来替换的
上面讲的 谓词
,你们肯定都会,而且觉得非常简单
但接下来要讲的,你们还会觉得简单吗
EXISTS
首先 EXISTS
也是 SQL 谓词
,那为什么不放到 谓词
那一章节下来讲?
因为 EXISTS
是主角嘛,主角,你们懂吗
主角最大嘛,戏份必须给足!
关于 EXISTS
,我们平时用的不多,甚至不用,不是说它适用场景少,而是它走的 海王海女
这种高端路线,我们很难驾驭!
它用法与其他谓词不一样,而且不好理解,另外很多情况下可以用 IN
来代替
但今天,我也带你们高端一回,体验下 海王海女
的感觉
在真正讲解 EXSITS
示例之前,我们先来了解下理论知识:实体的阶层 、全称量化与存在量化
实体的阶层
关于 阶
,我只能说很润,润到你心坎的那种
不信的话,你们可以看看:神奇的 SQL 之层级 → 为什么 GROUP BY 之后不能直接引用原表中的列
SQL 严格区分阶层,不能跨阶层操作
就用我们常用的谓词来举例,同样是谓词,但是与 = 、BETWEEN
等相比,EXISTS
的用法还是大不相同的,概括来说,区别在于 谓词的参数可以取什么值
例如 x = y
或 x BETWEEN y
等谓词可以取的参数是像 21
或者 李小龙
这样的单一值,我们称之为 标量值
,而 EXISTS
可以取的参数究竟是什么呢?从下面这条 SQL 语句来看,EXISTS
的参数不像是单一值
1 | sql复制代码SELECT * FROM tbl_student ts |
我们可以看出, EXISTS
的参数是行数据的集合
之所以这么说,是因为无论子查询中选择什么样的列,对于 EXISTS
来说都是一样的
在 EXISTS
的子查询里, SELECT 子句的列表可以有下面这三种写法
1 | html复制代码通配符:SELECT * |
也就是说如下 3 条 SQL 查到的结果是一样的
1 | sql复制代码-- SELECT * |
用个图来概括下 一般谓词
与 EXISTS
的区别
从上图我们知道,EXISTS
的特殊性在于输入值的阶数(输出值和其他谓词一样,都是逻辑值)
谓词逻辑中,根据输入值的阶数对谓词进行分类,=
或者 BETWEEEN
等输入值为一行的谓词叫作 一阶谓词
,而像 EXISTS
这样输入值为行的集合的谓词叫作 二阶谓词
,是不是高端起来了?
全称量化和存在量化
谓词逻辑中有量词(限量词、数量词)这类特殊的谓词,我们可以用它们来表达一些这样的命题:所有的 x 都满足条件 P
或者 存在(至少一个)满足条件 P 的 x
,前者称为 全称量词
,后者称为 存在量词
,分别记作 ∀
(A的下倒)、∃
(E的左倒)
SQL 中的 EXISTS
谓词实现了谓词逻辑中的 存在量词
,然而遗憾的是, SQL 却并没有实现 全称量词
但是没有全称量词并不算是 SQL 的致命缺陷,因为全称量词和存在量词只要定义了一个,另一个就可以被推导出来,具体可以参考下面这个等价改写的规则(德·摩根定律)
1 | html复制代码∀ x P x = ¬ ∃ x ¬P(所有的 x 都满足条件 P =不存在不满足条件 P 的 x ) |
因此在 SQL 中,为了表达全称量化,需要将 所有的行都满足条件 P
这样的命题转换成 不存在不满足条件 P 的行
不知道你们看懂了,我反正已经讲晕了
对理论晕了,你们先别慌,我们结合具体的实际案例来看看 EXISTS
的妙用
查询表中“不”存在的数据
tbl_student
中的学生都分配到了具体的班级,假设新来了两个学生(刘德华、张家辉),他们暂时还未被分配到班级,我们如何将他们查询出来(查询未被分配到班级的学生信息)
1 | sql复制代码-- 新来、未被分配到班级的学生 |
我们最容易想到的 SQL 肯定是下面这条
1 | sql复制代码-- NOT IN 实现 |
其实用 NOT EXISTS
也是可以实现的
1 | sql复制代码-- NOT EXISTS 实现 |
肯定 ⇔ 双重否定 转换
EXISTS
谓词来表达全称量化,这是 EXISTS
的用法中很具有代表性的一个用法
但是需要我们打破常规思维,习惯从全称量化 所有的行都××
到其双重否定 不××的行一行都不存在
的转换
假设我们有学生成绩表:tbl_student_score
1 | sql复制代码-- 学生成绩表 |
所有科目分数都在 50 分以上
查询所有科目分数都在 50 分以上的学生,这个 SQL 怎么写?
是不是有点懵,懵就对了,不然你们不会往下看了呀!
我们需要转换下命题,将查询条件 所有科目分数都在 50 分以上
转换成它的双重否定 没有一个科目分数不满 50 分
,然后用 NOT EXISTS
来表示转换后的命题
1 | sql复制代码-- 没有一个科目分数不满 50 分 |
是不是很简单?
数学分数在80分及以上且语文分数在50分及以上
查询出数学分数在 80 分以上(包含80)且语文分数在 50 分以上(包含)的学生,这 SQL 又该如何写?
这个条件是 全称量化
的条件吗
直观感觉不是,但如果改成:某个学生的所有行数据中,如果科目是数学,则分数在 80 分及以上;如果科目是语文,则分数在 50 分及以上
这是不是就是 全称量化
条件了?
接下来怎么办,肯定是进行双重否定转换呀,条件则是:某个学生的所有行数据中,如果科目是数学,则分数不低于 80;如果科目是语文,则分数不低于 50
那么我们就可以按如下顺序逐步写入满足条件的 SQL
1 | sql复制代码-- 1、CASE 表达式,肯定 |
我相信你们肯定没看懂,但你们也不用纠结,如果工作中你们真的遇到这样的需求,可以用如下方式实现
- 用编程语言,在内存中实现过滤嘛
- 把提需求的人干掉(个人不太推荐)
嵌套 EXISTS
有三张表
1 | sql复制代码DROP TABLE IF EXISTS `student`; |
如下 SQL 是查什么?
1 | sql复制代码SELECT Sname |
另外,如下两个需求,SQL 该怎么写
- 查询被所有学生选修的课程的课名
- 查询选修了 200215122 学生选修的全部课程的学生学号
感兴趣的可以试试,答案在:SQL 中的 EXISTS 到底做了什么? 或者 《数据库系统概论(第4版)》
总结
- SQL 中的谓词分两种:一阶谓词和二阶谓词(EXISTS),区别主要在于接收的参数不同,一阶谓词接收的是
行
,而二阶谓词接收的是行的集合
- SQL 中没有与
全称量词
相当的谓词,需要进行 双重否定 转换,然后用NOT EXISTS
实现 - EXISTS 之所以难用(不是不好用,而是不会用),主要是
全称量词
的命题转换(肯定 ⇔ 双重否定
)比较难,实际工作中往往会舍弃 EXISTS,寻找它的替代方式,可能是 SQL 的替代,也可能是业务方面的转换,所以说,EXISTS 掌握不了没关系,但是能掌握那是最好了
参考
《SQL基础教程》
《SQL进阶教程
本文转载自: 掘金