【概述】
一个 SELECT-WHERE
语句称为一个查询块,将一个查询块嵌套在另一个查询块的 WHERE
子句或 HAVING
短语的条件中,称为嵌套查询
在嵌套查询这种,上层的查询块称为外层查询或父查询,下层的查询块称为内层查询或子查询
如下例,就是一个嵌套查询
1 | SELECT Sname /*父查询*/ |
SQL 允许多层嵌套查询,即一个子查询中可以嵌套其他子查询,使得通过使用多个简单的查询构成复杂的查询,从而加强了 SQL 的查询能力,这种层层嵌套的方式,反映了 SQL 语言的结构化
要注意的是,有些嵌套查询可以通过连接运算替代,同时子查询不能使用 ORDER BY
子句,ORDER BY
子句只能对最终的结果排序
【带有 IN 的子查询】
在嵌套查询中,子查询的结果往往是一个集合,所以谓词 IN
是嵌套查询中最常用的谓词
例如,查询与张三在一个系的学生
该要求可以分步来完成,第一步确定张三所在的系名
第二步根据所查的系名来查找所有在 CS 系的学生
将第一步嵌入第二步中,即可构造嵌套查询
该例中,子查询的查询条件不依赖父查询,该类查询称为不相关子查询,一般处理的方法是根据要求从里向外逐层处理,用子查询的结果用于建立其父查询的查询条件
除上述两种方法外,该例还可以使用自身连接来完成
可见,对于嵌套查询来说,有些可以使用连接查询来替代,只是 SQL 语言的书写和执行效率不同
如果是嵌套查询:首先会在内层中筛选得到一张满足条件的小表,再到外面的一层表进行查找,运算量较少
如果是连接查询:首先会进行笛卡尔积运算,生成一张大表后再去进行选择,运算量较大
对于成千上万行的大型数据库,使用上述两种不同的方法运算效率差别极大,也就是说,使用嵌套查询可以大幅度提高查询效率,此外,嵌套查询逐步求解层次清楚、易于构造,具有结构化程序设计的优点
【带有比较运算符的子查询】
带有比较运算符的子查询是指父查询与子查询间用比较运算符连接,当用户确切知道子查询返回的是单值时,可以使用 >
、<
、>=
、<=
、=
、!=
、<>
等比较运算符
例如,找出每个学生超过其选修课程平均成绩的课程号
该例中,子查询的查询条件依赖父查询,该类查询称为相关子查询,一般处理的方法是:
- 取父查询中表的第一个元组,根据与子查询相关的属性值处理子查询,若
WHERE
子句返回为真,则将该元组放入结果表中 - 取父查询中表中的下一个元组,重复过程 1,直到父查询全部检查完为止
【带有 ANY、ALL 的子查询】
语义
子查询返回单值时可以用比较运算符,当返回多值时要用 ANY
谓词(有的系统使用 SOME
)或 ALL
谓词
ANY
代表任意一个值,ALL
代表所有值,当使用 ANY
或 ALL
时,必须同时使用比较运算符,其语义如下表所示:
谓词与修饰符 | 语义 |
---|---|
>ANY | 大于子查询结果中的某个值 |
>ALL | 大于子查询结果中的所有值 |
<ANY | 小于子查询结果中的某个值 |
<ALL | 小于子查询结果中的所有值 |
>=ANY | 大于等于子查询结果中的某个值 |
>=ALL | 大于等于子查询结果中的所有值 |
<=ANY | 小于等于子查询结果中的某个值 |
<=ALL | 小于等于子查询结果中的所有值 |
=ANY | 等于子查询结果中的某个值 |
=ALL | 等于子查询结果中的所有值 |
!=(或<>)ANY | 不等于子查询结果中的某个值 |
!=(或<>)ALL | 不等于子查询结果中的任何一个值 |
例如,查询其他系中比 CS 系所有学生年龄都小或相等的学生姓名、年龄
聚集函数
实际上,用聚集函数实现子查询通常比直接用 ANY
或 ALL
的查询效率高,ANY
、ALL
与聚集函数的对应关系如下表
= | != 或 <> | < | <= | > | >= | |
---|---|---|---|---|---|---|
ANY | IN | <MAX | <=MAX | >MIN | >=MIN | |
ALL | NOT IN | <MIN | <=MIN | >MAX | >=MAX |
【带有 EXISTS 的子查询】
EXISTS 谓词
EXISTS
谓词即数理逻辑中的存在量词 $\exists$,所有带 IN
、比较运算符、ANY
、ALL
谓词的子查询都能用带 EXISTS
谓词的子查询等价替换
带有 EXISTS
谓词的子查询不返回任何数,只产生逻辑真值 true
或 false
- 若子查询结果非空,则外层的
WHERE
子句返回true
- 若子查询结果为空,则外层的
WHERE
子句返回false
由于 EXISTS
返回逻辑真值,给出的列名无实际意义,因此尤其引出的子查询,其目标列表表达式通常都用 *
例如,查询选修 1 号课程的学生姓名
EXISTS
谓词通常也会与 NOT
搭配使用
- 若子查询结果非空,则外层的
WHERE
子句返回false
- 若子查询结果为空,则外层的
WHERE
子句返回true
例如,查询没有选修 1 号课程的学生姓名
全称量词的实现
SQL 中没有全称量词 $\forall$,但是利用谓词演算的方法,可以将带有全称量词的谓词转换为等价的带有存在量词的谓词,即:
例如,查询选修了全部课程的学生的学号、姓名
由于没有全称量词,转换为等价的用存在量词的形式,即查询没有一门课程是他不选修的学生的学号、姓名
1 | SELECT Sname |
逻辑蕴含的实现
SQL 中同样没有逻辑蕴含 $\rightarrow$,利用谓词演算,可以将逻辑蕴含进行等价转换,即
例如,查询至少选修了学号为 1 号的学生选修的全部课程的学生号
该查询用逻辑蕴含表达为:查询学号为 x 的学生,对所有课程 y,只要学号为 1 的学生选修了课程 y,则 x 也选修了 y
其中,形式化表达如下:
- 用 $p$ 表示谓词 “学号为 1 的学生选修了课程 y”
- 用 $q$ 表示谓词 “学生 x 选修了课程 y”
则上述查询为:$(\forall y)p \rightarrow q$
可以发现是一个蕴含式,将其进行等价变换:
变换后的语义为:不存在这样的课程 y,学号为 1 的学生选修了 y,而 学生 x 没有选
1 | SELECT DISTINCT Sno |