开心一刻
十年前,我:我交女票了,比我大两岁
妈:不行!赶紧分!
八年前,我:我交女票了,比我小两岁,外地的
妈:你就不能让我省点心?
五年前,我:我交女票了,市长的女儿
妈:别人还能看上你?分了吧!
今年,我挺着大肚子踏进家门
妈:闺女啊,你终于开窍了!
谓词
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进阶教程
本文转载自: 掘金