【3.18数据库作业8】SQL练习5 - SELECT(嵌套查询EXISTS、集合查询、基于派生表的查询)

-带有EXISTS谓词的子查询
-集合查询
-基于派生表的查询
-Select总结

下午上次课学到了嵌套查询-3
今天我们来看一下

带有EXISTS谓词的子查询

3/20 15:30
在这里插入图片描述
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中并且Cno=1,则取该元组的Sname送入结果表

由EXISTS引出的子查询,其目标表达式都用 *,如图
在这里插入图片描述
[例 3.61] 查询没有选修1号课程的学生姓名。

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

这一题和【例3.60】对比就好理解得多了
只需将EXISTS换成NOT EXISTS即可
【例3.60】中若外层WHERE语句返回真值,则【例3.61】外层WHERE语句返回假值,也就是说把选修了1号课程的学生过滤掉,剩下的全是没有选修一号课程的学生
看一下结果
在这里插入图片描述

所有带IN谓词、比较运算符、ANY和ALL谓词的子查询都能用带EXISTS谓词的子查询等价替换:
可见,EXISTS谓词的强大,能代替,但不一定用它最方便。但是EXISTS谓词查询不能全被其他的代替。两者不完全等价
[例 3.55]查询与“刘晨”在同一个系学习的学生。
同一个题存在上一个作业中【例3.55】中

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

先是子查询,依次取Sdept若与名字为刘晨的Sdept相等,则返回真值,
取该学生的Sno,Sname,Sdept送入结果表中

SQL语言中没有全称量词∀(FOR ALL),可以把带有全称量词的谓词转换为等价的带有存在量词的谓词:
(∀x)P≡¬(∃x(¬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) );

意思就是:没有一门课程是他不选修的,也就是选修了全部课程
执行过程

 1. 取Student中的第一个Sno值
 2. 取Course中的第一个Cno值
 3. 看SC中是否存在1中取得第一个Sno值且Cno=第一个Cno的,存在为F,不存在为T,这里因为是NOT EXISTS
 4. back->2,取Course中的第二个Cno值
 5. 看SC中是否存在Cno=第二个Cno值的(这里因为是同一个人所以不再确认Sno),存在为F,不存在为T,这里为F
 6. 再back->2,再取Course的第三个Cno值,再看SC中........
 7. 最终得到第二层结果(假设):T∨T∨T∨T∨T∨T∨T=T**(注意这是第二层的结果 并不是最终结果)*
 8. 得到第一层结果:F(取反)
 9. 所以Sno值为第一个值的第一个学生没有选修所有课

然后进入第一层:取Student中的第二个Sno值依次执行上述操作

直至Sno全部循环一遍 ,这个就是循环吼吼

SQL语言中没有蕴涵逻辑运算
可以利用谓词演算将逻辑蕴涵谓词等价转换为:
p→q≡¬p∨q

[例 3.63]查询至少选修了学生201215122选修的全部课程的学生号码。

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

1.理解题意:
查询学号为x的学生,对所有的课程y,只要201215122学生选修了课程y,则x也选修了y。
2.假设表示:
①:用p表示谓词 “学生201215122选修了课程y”
②:用q表示谓词 “学生x选修了课程y”
③:则上述查询为: (∀y)p→q
3.等价变换: (∀y)p→q≡¬∃y(p∧q)
4.变换后语义:不存在这样的课程y,学生201215122选修了y,而学生x没有选

执行过程
从外向内

1.取SCX中第一个201215121
2.第二层从SCY中取1
3.第三层(WHERE SCZ.Sno=201215121 AND SCZ.Cno=1)取值为T
4.back to 第二层(SCY.Sno=201215122AND F) 取值为F
5.第二层从SCY中取2
6.第三层:SCZ.Sno=201215121 AND SCZ.Cno=2 结果为T
再回到第二层.....
最终得到第二层结果 F
得到第一层结果 T
结论:201215121选修了201215122选修的全部课程

这里,DIDTINCT起的作用是取回唯一,不会重复。下面的以此类推

在这里插入图片描述

集合查询

集合操作的种类
并-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岁的学生的交集。
也就是找计算机科学系中小于等于19岁的学生
简单的话可以直接在Student中查询

SELECT *
FROM Student
WHERE Sdept= 'CS' AND  Sage<=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';

[例 3.68] 查询计算机科学系的学生与年龄不大于19岁的学生的差集。
这道题和【3.66】相比 这个题就是相反的呀
只要求计算机科学系中大于19岁的学生就好了

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

集合查询

SELECT *
FROM  Student
WHERE Sdept='CS'

EXCEPT

SELECT  *
FROM  Student
WHERE 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

看着好乱啊,我们来分析一一下
这里我们看到AS后 这里是一个表,名为Avg_sc,avg_sno,avg_grade分别是它的两个列名,实质为Sno,Avg(Grade)。
Avg_sc是一个临时派生表。

也可以这样写

SELECT Sno, Cno    /*相关子查询*/
FROM SC x
WHERE Grade>=(SELECT AVG(Grade) 
		      FROM  SC y
		      WHERE y.Sno=x.Sno);

如果子查询中没有聚集函数,
派生表可以不指定属性列,
子查询SELECT子句后面的列名为其缺省属性。

【例3.60】查询所有选修了1号课程的学生姓名。
可以由以下三种完成
一:

SELECT Sname
FROM Student,SC
WHERE SC.Sno=Student.Sno AND Cno='1';

这个最简单也容易理解,就是只要SC中的学生Cno有为‘1’的值,那就符合条件
二:

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

第二个利用了EXISTS谓词 若存在返回真值,该学生的Sname放入结果表
三:

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

第三个就是从SC 中选择Cno=‘1’的学号作为一个临时表SC1,然后将Sno=SC1.Sno作为条件,将符合条件的Sname放入结果表

3/21 11:37

SELECT总结

这一块每次写语句少不了的那肯定是SELECT,接下来我们来一起回顾一下数据查询这一块的知识吧!

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

如果直接回顾这一块的知识,最开始想起来的就是单表查询,然后里面有
选择表中若干列,
选择表中若干元祖,
然后就是连接查询,我想到的是等值连接和自然连接,记得自然连接是特殊的等值连接,去掉重复列,然后就有自身连接外连接,复杂一点就是多表连接
嵌套查询记得子查询和父查询之间有相关子查询和不相关子查询,还有各种子查询(带有IN谓词,带有比较运算符,ANY,SOME,ALL,带有EXISTS谓词…)

集合查询
并(UNION)交(INTERSECT)叉(EXPECT)
基于派生表的查询

这周还好,时间还算富裕,提前做了,本来都是赶到周日的时候下午的时候才开始做,今天周六就做完了,点个赞。

发布了14 篇原创文章 · 获赞 13 · 访问量 1978

猜你喜欢

转载自blog.csdn.net/summer__kkkk/article/details/104974064