【数据库原理】关系数据库标准语言SQL与关系数据库管理系统SQL Server(五)

子查询.

当我们在WHERE子句中包含了一个形如SELECT...FROM...WHERE...的查询块时,此查询块称为子查询或嵌套查询,而包含它的外部查询语句称为父查询。嵌套查询可以将一系列的简单查询构造成复杂查询,增强查询的能力。子查询的嵌套层次最多可以达到255层。
嵌套查询在执行时由里向外处理,每一个子查询是在其上一层的父查询处理之前就完成的,父查询需要用到子查询的结果。

普通子查询.

普通子查询的执行顺序是:首先执行子查询,然后把子查询的结果作为父查询的查询条件的值。普通子查询只执行一次,而父查询所涉及的所有记录都与其查询结果进行比较以确定查询结果集合。

  • 【返回一个值的普通子查询】当子查询的返回值只有一个时,可以用比较运算符将父查询和子查询连接起来。

【例】查询与刘伟老师职称相同的教师号、姓名。

SELECT TNo,TN
FROM T
WHERE Prof = (SELECT Prof FROM T WHERE TN='刘伟')

此查询相当于将查询分成两个查询块来执行,首先执行的是子查询:

SELECT Prof 
FROM T 
WHERE TN = '刘伟'

该子查询返回一个值,即刘伟老师的职称——‘讲师’,然后以此作为父查询的条件,再执行父查询,查找职称为讲师的教师号以及姓名。

  • 【返回一组值的普通子查询】如果子查询的返回值不止一个,而是一个集合时,我们就不能直接使用比较运算符来表示查询条件,而需要借助关键字ANYALL.

【例】查询讲授课程C5的教师的姓名。

SELECT TN 
FROM T
WHERE TNo = ANY (SELECT TNo FROM TC WHERE CNo='C5')

首先执行子查询,找到讲授课程C5的那些教师的教师号的集合,而后执行父查询,只要TNo等于子查询结果集合中的任意一个值,都说明该教师讲授课程,就将其对应的TN放入最终查询结果。当然这一查询也可以通过连接操作来实现:

SELECT TN
FROM T,TC
WHERE T.TNo=TC.TNo AND CNo='C5'

【例】查询其它系中比计算机系某一教师工资高的教师的姓名和工资。

SELECT TN,Sal
FROM T
WHERE Sal > ANY(
				   SELECT Sal FROM T WHERE Dept='计算机'
				 ) 
	  AND Dept!='计算机'

从逻辑角度来理解,高于计算机系某一教师的工资,只需要高于计算机系教师的最低工资即可,所以我们可以使用下面这段代码:

SELECT TN,Sal
FROM T
WHERE SaL > (SELECT MIN(Sal) FROM T WHERE Dept='计算机')
	  AND Dept!='计算机'

我们首先利用子查询以及MIN()函数找到计算机系的最低工资x,而后执行父查询,找出不是计算机系的教师中工资高于x的教师的姓名及其工资。

【例】查询讲授课程C5的教师姓名,使用关键字IN.

SELECT TN
FROM T
WHERE TNo IN(SELECT TNo FROM TC WHERE CNo='C5')

【例】查询其他系中比计算机系所有教师工资都高的教师的姓名和工资。

SELECT TN,Sal
FROM T
WHERE Sal >ALL(SELECT SAL FROM T WHERE Dept = '计算机')
	  AND Dept!='计算机'

和上面一个使用库函数MIN()的例子一样,我们也可以使用库函数MAX()来实现比所有的计算机系教师工资都高这一条件:

SELECT TN,Sal
FROM T
WHERE Sal >(SELECT MAX(Sal) FROM T WHERE Dept='计算机')
	  AND Dept!='计算机'

相关子查询.

前面所有的子查询均为普通子查询,这些子查询和父查询之间除了父查询会使用子查询的结果作为查询条件以外,再无交互。但是,有时子查询的查询条件需要引用父查询表中的属性值,我们将这类查询称为相关子查询。
与普通子查询相比,相关子查询的执行顺序复杂了很多。首先我们选取了父查询表中的第一行记录,内部的子查询利用此行中相关的属性值进行查询,而后父查询再根据子查询返回的结果判断此行是否满足查询条件。如果满足条件,则把此行放入父查询的查询结果集合中。重复执行这一过程,直到处理完父查询表中的每一行数据。由此我们也可以看出,相关子查询的执行次数是由父查询表的行数决定的。

【例】查询不讲授课程C5的教师姓名。

SELECT DISTINCT TN
FROM T
WHERE 'C5'!=ALL(SELECT CNo FROM TC WHERE TNo=T.TNo)

!=ALL的含义是不等于子查询结果中的任何一个值,也可以使用NOT IN代替。我们依据第27页的关系表,来叙述这一相关子查询的过程。首先选择父关系表T中的一行,是【T1、李力、男、47、教授、1500、3000、计算机】这一条记录,而后进入子查询,查找出的CNo集合为{C1、C4},其中并没有包括C5,所以该记录符合查询条件,我们将其投影到TN列,也就是记录【李力】进入最终的结果集。后续的过程也是如此。
使用EXISTS关键字也可以进行相关子查询,EXISTS是表示存在的量词,带有该关键字的子查询不会返回任何结果,而是一个布尔值True或False.

【例】查询讲授课程C5的教师的姓名,使用EXISTS关键字。

SELECT TN
FROM T
WHERE EXISTS(SELECT * FROM TC WHERE TNo=T.TNo AND CNo='C5')

当子查询的TC表中存在一行记录满足条件时,父查询就将此时T表中的这一个元组投影到TN列,并将其放入结果集。

【例】查询没有讲授课程C5的教师姓名,使用EXISTS关键字。

SELECT TN
FROM T
WHERE (NOT EXISTS(SELECT * FROM TC WHERE TNo=T.TNo AND CNo='C5'))

【例】查询选修了所有课程的学生的姓名。

SELECT SN
FROM S
WHERE NOT EXISTS
(
	SELECT *
	FROM C
	WHERE NOT EXISTS
	(
		SELECT *
		FROM SC
		WHERE SNo=S.SNo AND CNo=C.CNo
	)
)	 

选修了所有课程的同学也可以理解为:对于该同学x来说,不存在这样的课程y,在选课表SC中不存在<x,y>这对组合的记录。
在第27页的关系表中加入了数据,使得确实存在选修了所有课程的同学,表如下所示,S1同学选修了所有7门课:
在这里插入图片描述
执行结果如下所示:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44246009/article/details/108049756