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))