mysql 子关联查询之 EXISTS 和 NOT EXISTS

MySQL EXISTS 和 NOT EXISTS 子查询语法如下:

SELECTFROM table WHERE EXISTS (subquery)
  

该语法可以理解为:将主查询的数据,放到子查询中做条件验证,根据验证结果(TRUE 或 FALSE)来决定主查询的数据结果是否得以保留。

 

范例一:

下面以实际的例子来理解 EXISTS 子查询。下面是原始的数据表:

article 文章表:

aidtitlecontentuid
1文章1文章1正文内容…1
2文章2文章2正文内容…1
3文章3文章3正文内容…2
4文章4文章4正文内容…4

user 用户表:

uidtitlecontent
1adminadmin@5idev.com
2小明xiao@163.com
3Jackjack@gmail.com

我们要查出 article 表中的数据,但要求 uid 必须在 user 表中存在。SQL 语句如下:

SELECT * FROM article WHERE EXISTS (SELECT * FROM user WHERE article.uid = user.uid)
  

返回查询结果如下:

aidtitlecontentuid
1文章1文章1正文内容…1
2文章2文章2正文内容…1
3文章3文章3正文内容…2

从语句执行结果可以看出,article 表中第 4 条记录没有被保留,原因就是该条记录的数据在子查询中返回的结果是 FALSE 。 
当上面的 SQL 使用 NOT EXISTS 时,查询的结果就是 article 表中 uid 不存在于 user 表中的数据记录。

 

范例二:

我们先介绍下使用的3个数据表:

student数据表:

sno 学号snamessexsage
20161181Altair20
20161182Desmond18
20161183Ezio22
20161184Christina19

course数据表:

cno 课程编号cname 课程名
1C语言
2数据结构
3信号与系统
4模拟电子技术
5高数

sc数据表:

sno 学号cno 课程编号grade 成绩
20161181199
20161182298
20161181297
20161181395
20161184392
20161181490
20161181588
20161183558

EXISTS

EXISTS代表存在量词∃。带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值“true”或者逻辑假值“false”。

 

查询选修了课程”信号与系统“的同学 

其他方式:

               还可以通过三表联查的方式查询(join)


  
  1. SELECT s.Sname FROM student s
  2. WHERE EXISTS
  3. ( SELECT * FROM sc, course c WHERE sc.sno = s.sno AND sc.cno = c.cno AND c.cname = '信号与系统')

使用存在量词EXISTS后,若内层查询结果为非空,则外层的WHERE子句返回值为真,否则返回值为假。

在本例中,首先分析最内层的语句:

SELECT * FROM sc, course c WHERE sc.sno = s.sno AND sc.cno = c.cno AND c.cname = '信号与系统'
  

本例中的子查询的查询条件依赖于外层父查询的某个属性值(本例中的是Student的Sno值),这个相关子查询的处理过程是:

首先取外层查询中(student)表的第一个元组,根据它与内层查询相关的属性值(Sno值)处理内层查询,若外层的WHERE返回为真,则取外层查询中该元组的Sname放入结果表;

然后再取(student)表的下一组,重复这一过程,直至外层(Student)表全部检查完毕。

查询结果表:

Sname
Altair
Christina

NOT EXISTS

与EXISTS谓词相对的是NOT EXISTS谓词。使用存在量词NOT EXISTS后,若对应查询结果为空,则外层的WHERE子语句返回值为真值,否则返回假值。

 查询没有选修课程”信号与系统“的同学


  
  1. SELECT s.Sname FROM student s
  2. WHERE NOT EXISTS
  3. ( SELECT * FROM sc, course c WHERE sc.sno = s.sno AND sc.cno = c.cno AND c.cname = '信号与系统')

使用NOT EXISTS之后,若内层查询结果为非空,则对应的NOT EXISTS不成立,所以对应的WHERE语句也不成立。

范例中李勇同学对应的记录符合内层的select语句的,所以返回该记录数据,但是对应的NOT EXISTS不成立,WHERE语句也不成立,表示这不是我们要查询的数据。

查询结果表:

Sname
Desmond
Ezio

 

查询选修了全部课程的学生姓名:(这是一个用NOT EXISTS表示全称量词的例子)

其他方式:

               可以查询sc数据表  group by sno 学号 having count (sno 学号) >  course数据表全部课程的数量来查询


  
  1. SELECT Sname
  2. FROM Student
  3. WHERE NOT EXISTS
  4. ( SELECT * FROM Course WHERE NOT EXISTS
  5. ( SELECT * FROM SC WHERE Sno=Student.Sno AND Cno=Course.Cno)
  6. );

这个算是一个比较复杂的sql语句了,两个EXISTS和三个WHERE。

这个sql语句可以分为3层,最外层语句,最内层语句,中间层语句。

我们很关心最外层语句,因为结果表中的数据都是最外层的查询的表中的数据,我们更关心最内层的数据,因为最内层的数据包含了全部的判断语句,决定了student表中的那一条记录是我们查询的记录。

我们由内而外进行分析:

最外层的student表中的第一条记录是Altair同学对应的记录,然后中间层的course表的第一条记录是数据库对应的记录,然后对该数据进行判断(最内层的WHERE语句),结果返回真,则内层的NOT EXISTS为假, 
然后继续对course表中的下一条记录进行判断,返现NOT EXISTS的值也为假,直到遍历完course表中的所有的数据,内层的NOT EXISTS的值一直都是假,所以中间层的WHERE语句的值也一直都是假。 
对应student的Altair记录,course表中的所有的记录对应的中间层的返回值为假,所以最外层的NOT EXISTS对应的值为真,最外层的WHERE的值也为真,则Altair对应的记录符合查询条件,装入结果表中。 
然后继续对student表中的下一条记录进行判断,直达student表中的所有数据都遍历完毕。

 

先取一条student记录,进入中层,再取一条course的记录,进入内层,此时student的记录和course的记录,作为内层判断的条件,比如此时我取的第一条记录是Altair,那么我里面的Sql就可以写成


  
  1. SELECT * FROM Course WHERE NOT EXISTS
  2. ( SELECT * FROM SC WHERE Sno = '20161181' AND Cno=Course.Cno)
  3. )

此处 sno 20161181即Altair的学号,这条sql的意思是选出没有被Altair选择的课程,如果不存在,则返回false,再跟最外层的NOT EXISTS关联,负负得正。每一条循环的意思就是指,筛选出的每一个学生都不存在没有被他选取的那门课,即选了所有课。

最终查询结果:

Sname
Altair

参考:https://blog.youkuaiyun.com/qq_27571221/article/details/53090467 

### MySQLNOT EXISTSNOT IN 的区别 #### 基本概念 NOT EXISTS NOT IN 都用于过滤不符合条件的数据,但在内部工作方式上存在显著差异。 - **NOT EXISTS**: 使用查询来判断是否存在符合条件的记录。如果查询的结果为空,则返回 TRUE;否则返回 FALSE。其逻辑基于布尔表达式的评估[^1]。 - **NOT IN**: 主要通过匹配主查询中的列值是否存在于查询结果集中来进行筛选。它依赖于查询产生的具体结果集,并逐一比较这些值[^4]。 #### 执行机制对比 对于 NOT EXISTS 而言,它的执行计划通常更高效,因为它不需要完全计算出查询的所有可能结果即可完成操作。例如,在以下 SQL 查询中: ```sql SELECT * FROM user AS a WHERE NOT EXISTS ( SELECT b.ID FROM student AS b WHERE a.ID = b.ID ); ``` 这里,一旦发现某个 `a.ID` 对应的 `b.ID` 存在,就会立即停止对该特定行的进一步处理并跳到下一行数据继续验证[^2]。 而针对 NOT IN 实现相同功能时: ```sql SELECT * FROM user AS a WHERE a.ID NOT IN ( SELECT ID FROM student ); ``` 此情况下,整个查询 `(SELECT ID FROM student)` 将被一次性全部加载入内存形成临时表后再逐条比对每一个来自外部查询(`user`)里的ID值是否有落入该集合之中[^3]。 这种行为可能导致性能瓶颈特别是在面对大量重复或者 NULL 数据项的时候因为任何包含NULL值得字段都会让最终判定变得复杂甚至错误。 #### 关于 NULL 处理的不同表现 另一个值得注意的地方在于如何对待含有 null 值的情况: 当使用 not in 并且查询中有null出现时,即使其他所有的数值都不相等也会由于无法确定这个特殊的'?'(代表未知数即null)到底是不是等于当前正在考察的那个元素而导致整体否定命题失败从而不会选取这条记录出来显示给用户看. 然而not exists 则不会有这样的困扰,只要能够找到哪怕只有一个配对成功的例就足以证明存在关系成立进而使得外层where条件下的negation部分得到满足继而去掉那些确实不存在关联的对象之外的一切东西. #### 性能考量 一般而言,在大多数场景下推荐优先考虑采用 EXIST/NOT EXISTS 方式编写此类涉及多表联接运算的需求语句形式而非单纯依靠简单的成员资格测试(IN/NOT IN),尤其是在涉及到大规模数据量以及可能存在缺失情况(nulls)的应用场合当中更是如此. 尽管现代数据库引擎优化器已经非常强大可以自动转换某些类型的in/not in结构成为相应的exist版本以便提升效率但是手动调整仍然不失为一种良好实践习惯有助于确保程序运行稳定性可预测性同时减少不必要的资源消耗. ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值