数据库语言的练习5 - SELECT(嵌套查询EXISTS、集合查询、基于派生表的查询)(2020.3.18作业)

EXISTS谓词:

存在量词 \exists

带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值“true”或逻辑假值“false”。
若内层查询结果非空,则外层的WHERE子句返回真值
若内层查询结果为空,则外层的WHERE子句返回假值

由EXISTS引出的子查询,其目标列表达式通常都用 * ,因为带EXISTS的子查询只返回真值或假值,给出列名无实际意义。

NOT EXISTS谓词:
若内层查询结果非空,则外层的WHERE子句返回假值
若内层查询结果为空,则外层的WHERE子句返回真值

[例 3.60]查询所有选修了1号课程的学生姓名。

SELECT Sname
FROM   Student
WHERE  EXISTS(
		 SELECT *
		 FROM SC
		 WHERE Sno=Student.Sno AND Cno= '1');

按顺序取Student中的Sno值,看SC中存不存在这个Sno值,也就是看这个同学有没有选课,如果存在并且其Cno= ‘1’,则不为空,取此Student.Sname送入结果表
在这里插入图片描述
[例 3.61] 查询没有选修1号课程的学生姓名。

SELECT Sname
FROM   Student
WHERE  NOT EXISTS(
		SELECT *
		FROM SC
		WHERE Sno = Student.Sno AND Cno='1');

NOT EXISTS就是取反操作
在这里插入图片描述
所有带IN谓词、比较运算符、ANY和ALL谓词的子查询都能用带EXISTS谓词的子查询等价替换:
[例 3.55]查询与“刘晨”在同一个系学习的学生。
可以用带EXISTS谓词的子查询替换:

SELECT Sno,Sname,Sdept
FROM   Student S1
WHERE  EXISTS(
		SELECT *
        FROM Student S2
        WHERE S2.Sdept = S1.Sdept AND S2.Sname = '刘晨');

按顺序取Student中的Sdept值,看这个Sdept值跟刘晨的Sdept值是否相等。相等就是存在,则不为空,取此学生的Sno,Sname,Sdept送入结果表。

SQL语言中没有全称量词 \forall ,可以把带有全称量词的谓词转换为等价的带有存在量词的谓词:
( x ) P ¬ ( x ( ¬ P ) ) (\forall x)P \equiv \neg (\exists x(\neg P))

[例 3.62] 查询选修了全部课程的学生姓名。

SELECT Sname
FROM   Student
WHERE  NOT EXISTS(
	SELECT *
	FROM Course
	WHERE NOT EXISTS(
			SELECT *
			FROM SC
			WHERE Sno= Student.Sno AND Cno= Course.Cno) );

转义后的表达:没有一门课程是他不选修的
过程:
进入第一层:取Student中的第一个Sno值201215121
进入第二层:取Course中的第一个Cno值1
进入第三层:看SC中是否存在Sno=201215121且Cno=1的(Sno= Student.Sno AND Cno= Course.Cno),存在为F,不存在为T(注意是NOT EXISTS取反),这里为F
返回第二层:取Course中的第二个Cno值2
进入第三层:看SC中是否存在Sno=201215121且Cno=2的(Sno= Student.Sno AND Cno= Course.Cno),存在为F,不存在为T,这里为F
……
返回第二层:取Course中的最后一个Cno值7
进入第三层:看SC中是否存在Sno=201215121且Cno=7的(Sno= Student.Sno AND Cno= Course.Cno),存在为F,不存在为T,这里为T
得到第二层结果:F \vee F \vee F \vee T \vee T \vee T \vee T=T
得到第一层结果:F(取反)
所以Sno值为201215121的第一个学生没有选修所有课
进入第一层:取Student中的第二个Sno值201215122
……
以此类推
这里虽然有些麻烦但其实是很好理解的
在这里插入图片描述
在这里插入图片描述
SQL语言中没有蕴涵逻辑运算
可以利用谓词演算将逻辑蕴涵谓词等价转换为:
p q ¬ p q p \rightarrow q \equiv \neg p \vee q

[例 3.63]查询至少选修了学生201215122选修的全部课程的学生号码。
用逻辑蕴涵表达:
查询学号为x的学生,对所有的课程y,只要201215122学生选修了课程y,则x也选修了y。
形式化表示:
用P表示谓词 “学生201215122选修了课程y”
用q表示谓词 “学生x选修了课程y”
则上述查询为: ( y ) p q (\forall y) p \rightarrow q
等价变换: ( y ) p q ¬ y ( p q ) (\forall y) p \rightarrow q \equiv \neg \exists y(p \wedge q)
变换后语义:不存在这样的课程y,学生201215122选修了y,而学生x没有选

SELECT DISTINCT Sno
FROM   SC SCX
WHERE  NOT EXISTS(
		SELECT *
        FROM SC SCY
        WHERE SCY.Sno = '201215122'  AND  NOT EXISTS(
        										SELECT *
                                     			FROM SC SCZ
                                    			WHERE SCZ.Sno=SCX.Sno AND SCZ.Cno=SCY.Cno));

过程:
进入第一层:取SCX中的第一个Sno值201215121
进入第二层:取SCY中的第一个Cno值1
进入第三层:看SCZ中是否存在Sno=201215121且Cno=1的(SCZ.Sno=SCX.Sno AND SCZ.Cno=SCY.Cno),存在为F,不存在为T,这里为F
返回第二层:看SCY中是否存在Sno=201215121且F(WHERE SCY.Sno = ‘201215122’ AND F[这里的F是上一步的结果]),存在为F,不存在为T,这里为F
取SCY中的第二个Cno值2
进入第三层:看SCZ中是否存在Sno=201215121且Cno=2的(SCZ.Sno=SCX.Sno AND SCZ.Cno=SCY.Cno),存在为F,不存在为T,这里为F
返回第二层:看SCY中是否存在Sno=201215121且F(WHERE SCY.Sno = ‘201215122’ AND F),存在为F,不存在为T,这里为F
取SCY中的第三个Cno值3
进入第三层:看SCZ中是否存在Sno=201215121且Cno=3的(SCZ.Sno=SCX.Sno AND SCZ.Cno=SCY.Cno),存在为F,不存在为T,这里为F
返回第二层:看SCY中是否存在Sno=201215121且F(WHERE SCY.Sno = ‘201215122’ AND F),存在为F,不存在为T,这里为F
取SCY中的第四个Cno值5
进入第三层:看SCZ中是否存在Sno=201215121且Cno=5的(SCZ.Sno=SCX.Sno AND SCZ.Cno=SCY.Cno),存在为F,不存在为T,这里为T
得到第二层结果:F \vee F \vee F \vee F \vee T=T
得到第一层结果:F
所以Sno值为201215121的第一个学生没有全部选修学生201215122选修的全部课程
进入第一层:使用了关键词DISTINCT,跳过重复的,取Student中的下一个Sno值201215122
……
以此类推
在这里插入图片描述
集合操作的种类:
并-UNION
交-INTERSECT
差-EXCEPT

参加集合操作的各查询结果的列数必须相同,对应项的数据类型必须相同
UNION:将多个查询结果合并起来时,系统自动去掉重复元组
UNION ALL:将多个查询结果合并起来时,保留重复元组
[例 3.64] 查询计算机科学系的学生及年龄不大于19岁的学生。

SELECT *
FROM  Student
WHERE Sdept= 'CS'
 
UNION
        
SELECT *
FROM  Student
WHERE Sage<=19;

在这里插入图片描述
[例 3.65] 查询选修了课程1或者选修了课程2的学生。

SELECT Sno
FROM   SC
WHERE  Cno='1'

UNION

SELECT Sno
FROM   SC
WHERE  Cno='2';

在这里插入图片描述
[例3.66] 查询计算机科学系的学生与年龄不大于19岁的学生的交集。

SELECT *
FROM  Student
WHERE Sdept='CS' 

INTERSECT

SELECT *
FROM  Student
WHERE Sage<=19 

在这里插入图片描述
就是查询计算机科学系中年龄不大于19岁的学生,可以用连接查询解决

SELECT *
FROM Student
WHERE Sdept= 'CS' AND  Sage<=19;

[例 3.67]查询既选修了课程1又选修了课程2的学生。

SELECT Sno
FROM   SC
WHERE  Cno='1'
 
INTERSECT

SELECT Sno
FROM   SC
WHERE  Cno='2';

在这里插入图片描述
也可以表示为嵌套查询:

SELECT Sno
FROM   SC
WHERE  Cno='1' AND Sno IN(
					SELECT Sno
                    FROM   SC
                    WHERE  Cno='2');

[例 3.68] 查询计算机科学系的学生与年龄不大于19岁的学生的差集。

SELECT *
FROM  Student
WHERE Sdept='CS'

EXCEPT

SELECT  *
FROM  Student
WHERE Sage <=19;

在这里插入图片描述
实际上是查询计算机科学系中年龄大于19岁的学生

SELECT *
FROM Student
WHERE Sdept= 'CS' AND  Sage>19;

子查询不仅可以出现在WHERE子句中,还可以出现在FROM子句中,这时子查询生成的临时派生表成为主查询的查询对象。

[例3.57]找出每个学生超过他自己选修课程平均成绩的课程号

SELECT Sno,Cno
FROM   SC,(SELECT   Sno,Avg(Grade) 
           FROM     SC 
		   GROUP BY Sno)
		   AS       Avg_sc(avg_sno,avg_grade)
WHERE  SC.Sno = Avg_sc.avg_sno AND SC.Grade >=Avg_sc.avg_grade

其中嵌套的查询结果为一张临时派生表,可以看为一张实际的表。将该表起名为Avg_sc,Sno,Avg(Grade)的列名分别为avg_sno,avg_grade
在这里插入图片描述
如果子查询中没有聚集函数,
派生表可以不指定属性列,
子查询SELECT子句后面的列名为其缺省属性。

[例]查询所有选修了1号课程的学生姓名,可以用如下查询完成:

SELECT Sname
FROM   Student,(SELECT Sno FROM SC WHERE Cno='1') 
				AS SC1
WHERE  Student.Sno=SC1.Sno;

先进行查询,查询结果为一张临时派生表,再将两表连接
在这里插入图片描述
与之前的方法对比,之前是先进行连接再进行查询,这种方法是先进行查询,将符合条件的内容构成一张临时派生表,再进行连接。感觉这种方法会更快捷,高效

SELECT语句的一般格式

SELECT [ALL|DISTINCT]<目标列表达式> [别名] [ ,<目标列表达式> [别名]]FROM            <表名或视图名> [别名] 
                [,<表名或视图名> [别名]]|(<SELECT语句>)[AS]<别名>
[WHERE <条件表达式>]
[GROUP BY <列名1>[HAVING<条件表达式>]]
[ORDER BY <列名2> [ASC|DESC]];

DISTINCT:去除重复值;
自身连接时要用到别名;
FROM中可有子查询,用AS生成一张临时派生表;
WHERE中的条件表达式是最复杂的部分,单表查询、连接查询、嵌套查询、集合查询、基于此派生表的查询。其中嵌套查询又最为复杂;
分组要用GROUP BY,HAVING+条件;
排序用ORDER BY,ASC为升序,DESC为降序,缺省为ASC。

总结:最后看着SELECT语句的一般格式就能回想起这一阵所学的内容。确实今天学习的嵌套查询EXISTS是最难理解的,但是老师说难也不能怂,趁热把这些东西又仔细的看了看,保证自己理解通透,发现它一层一层的还是很有规律的,反正比物理啥的简单多了
在这里插入图片描述

发布了8 篇原创文章 · 获赞 11 · 访问量 5457

猜你喜欢

转载自blog.csdn.net/MooM_X/article/details/104947168