SQL 中含 exists 的嵌套查询

1. 什么是 SQL 中的嵌套查询

SQL 中的嵌套查询就是一个 select-from-where 语句套在另一个查询语句中。

P.S. select-from-where 子句讲解

执行过程首先将 from 中的多张表通过笛卡尔积变成一张表(若只有一张表就用该表),依次拿出这张表的一个元组,去执行 where 中的判断,如果判断结果为 true 则将该条放入结果集,为 false 则将该条丢弃。

2. SQL 中的 exists

SQL 中用exists谓词来测试一个子查询返回的集合是否为空,空返回 True,非空返回 False。用not exists谓词来测试一个子查询返回的集合是否非空,非空返回 True,空返回 False。

具体说来,它们能怎么用呢?

2.1. 用途 1

通过相关子查询在 where 子句中嵌套一层exists 子句,来实现逐一检查表中的元组是否符合一定的条件。例子如下:

1
查询所有选修了 1 号课程的学生学号

语句如下:

1
2
3
4
5
select sno
from student as s
where exists(select *
from sc
where s.sno=sc.sno and cno=1)

怎么想到写出这样的查询(可以看着思路再回去看 SQL 语句)

我们的思路就是逐一检查 student 表中的学生挑选出选择了 1 号课程的学生(one by one),因此在 where 子句中就应想办法表达出选择 1 号课程这个判断条件,在 SQL 中我们可以用 exists 来表示存在该学生选了 1 号课程

这里需要注意的是

1
select * from sc where s.sno=sc.sno and cno=1

和普通的查询无异,找到就返回元组(说明该学生选了 1 号课程),找不到就不返回元组(说明该学生没有选 1 号课程)。

2.2. 用途 2

not exists来实现关系代数中的除法

1
查询选修了所有课程的学生的学号

这个如果用关系代数可以很简单地表达,就是SC÷C。但是 SQL 中没有办法直接做除法。

我们可以等价的将题目表述为,要查询的学生不存在任何一门没选修的课程,注意,这里的不存在任何一门没选修的课程就是 where 中应该填入的条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
--第一步
select sno
from student as s
where not exists(任何一门没选修的课程)
--p.s. not exists(任何一门没选修的课程)=不存在任何一门课没选修
--第二步 查找该生没选修的课程
任何一门没选修的课程可以表示为
select *
from course as c --从整个 course 中查找体现了任何一门课程
where not exists(选修过该门课程)
--第三步
选修过该门课程就是
select *
from sc
where s.sno=sno and c.cno =cno
--第四步,把代码放到汉字的位置,用一个 SQL 语句表达为
select sno
from student as s
where not exist(select *
from course as c
where not exists(select*
from sc
where s.sno=sno
and c.cno=cno))

同时,我们可以换一个思路,从集合的角度来看,该生选择了所有的课程就是全部课程这个集合是该生选的课的集合的子集。怎么把判断子集与exists结合起来呢?

我们知道exists是判断后面跟的集合是否为空集,而 A 是 B 的子集在数学上等价于 A 与 B 的差集为空集。因此我们可以判断 A 与 B 的差集是否为空即可。

因此,上面的 SQL 语句可以等价的表达为:

1
2
3
4
5
6
7
8
select sno
from student as s
where not exists((select cno
from course)
except --excpet 表示两个集合的差运算
(select cno
from sc
where sc.sno=s.sno))