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

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

学完查询啦!!嘻嘻嘻这周系统看一遍前边,写了博客真的蛮方便!那俺开始了。


带有EXISTS谓词的子查询

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

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

注⬇
带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值“true”或逻辑假值“false”。
由EXISTS引出的子查询,其目标列表达式通常都用 * ,因为带EXISTS的子查询只返回真值或假值,给出列名无实际意义。

【例3.60】查询所有选修了1号课程的学生姓名。
本查询涉及Student和SC关系,
在Student中依次取每个元组的Sno值,用此值去检查SC表,
若SC中存在这样的元组,其Sno值等于此Student.Sno值,并且其Cno= ‘1’,则取此Student.Sname送入结果表。

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

【例3.61】查询没有选修1号课程的学生姓名。
在Student中依次取每个元组的Sno值,用此值去检查SC表,
若SC中存在这样的元组,其Sno值等于此Student.Sno值,并且其Cno= ‘1’(内层查询为非空),则WHERE取假,结果不符合。
若SC中不存在这样我元组(内层为空),WHERE取真,这时取此Student.Sname送入结果表。

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

在这里插入图片描述
不同形式的查询间的替换
一些带EXISTS或NOT EXISTS谓词的子查询不能被其他形式的子查询等价替换。
所有带IN谓词、比较运算符、ANY和ALL谓词的子查询都能用带EXISTS谓词的子查询等价替换。

【例 3.55】查询与“刘晨”在同一个系学习的学生。
可以用带EXISTS谓词的子查询替换:
本查询涉及Student S1 S2关系,
在Student S1中依次取每个元组的Sno值,用此值去检查S2表,
若S2中的元组的Sdept值等于此S1.Sdept值,并且S2.name= ‘刘晨’,则取此S1.Sno,Sname,Sdept送入结果表。

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

在这里插入图片描述
★★★用EXISTS/NOT EXISTS实现全称量词★★★
SQL语言中没有全称量词 \forall (For all)
可以把带有全称量词的谓词转换为等价的带有存在量词的谓词:
( x ) P ¬ ( x ( ¬ P ) ) (\forall x)P \equiv \neg (\exists x(\neg P))

【例3.62】查询选修了全部课程的学生姓名。
转义后的表达:没有一门课程是他不选修的。

相关子查询:从外向内
1.在Student中取201215121
2.到第二层,Course取2
3.到第三层,WHERE Sno=201215121 AND Cno=1 T
4.返回二层,Course=2
5.到第三层,WHERE Sno=201215121 AND Cno=2 T
……
得到第二层结果(因为是NOT EXISTS所以相反)F∨F∨F∨T∨T∨T∨T=T
得到第一层结果(同上)F
201215121没有选修全部。
在Student中取201215122,以此类推。

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

在这里插入图片描述
★★★用EXISTS/NOT EXISTS实现逻辑蕴涵★★★
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∧\neg q)
变换后语义:不存在这样的课程y,学生201215122选修了y,而学生x没有选。

相关子查询:从外向内
1.从SCX取201215121
2.二层SCY取1
3.三层WHERE SCZ.Sno=201215121 AND SCZ.Cno=1 T
4.返回二层 SCY.Sno=‘201215122’ AND F 结果:F
5.二层SCY取2
6.三层:SCZ.Sno=201215121 AND SCZ.Cno=2 T
7.返回二层 SCY.Sno=‘201215122’ AND F 结果:F
8…二层SCY取3
6.三层:SCZ.Sno=201215121 AND SCZ.Cno=3 T
7.返回二层 SCY.Sno=‘201215122’ AND F 结果:F
……
得到第二层结果 F
得到第一层结果 T
结论:201215121选修了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));

在这里插入图片描述


集合查询

集合操作的种类
并-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';

【例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】找出每个学生超过他自己选修课程平均成绩的课程号
第一个查询相当于重新创建了一个学生平均成绩的表,然后进行连接,再查询符合条件的。
第二个就是嵌套查询的相关子查询,先取SC x的第一个Sno,然后在y里求平均成绩,再跟x的Grade比较,最后返回符合条件的Sno,Cno。

个人倾向第二个(因为好写,标准懒蛋思想),理解倒是两个都挺好理解的。

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


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

在这里插入图片描述
如果子查询中没有聚集函数
派生表可以不指定属性列,
子查询SELECT子句后面的列名为其缺省属性。

【例3.60】查询所有选修了1号课程的学生姓名。
第一个就是先查询Student的第一个Sno的Cno是否是1(SC表内),如果是返回T,返回Sno的Sname,不是就下一个元组。
第二个是把SC表内Cno=1的查询生成派生表,再查询两个表学号相等的,返回Sname。
第三个就先把Student和SC连接,然后找Cno=1的。

我觉得相比之下第三个更好理解!

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

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

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

在这里插入图片描述


SELECT总结

SELECT [ALL|DISTINCT] 
	<目标列表达式> [别名] [ ,<目标列表达式> [别名]]FROM <表名或视图名> [别名] 
     [ ,<表名或视图名> [别名]]|(<SELECT语句>)[AS]<别名>
[WHERE <条件表达式>]
[GROUP BY <列名1>[HAVING<条件表达式>]]
[ORDER BY <列名2> [ASC|DESC]];
  • 单表查询
    • 选择表中的若干列
      1.查询指定列
      2.查询全部列
      3.查询经过计算的值
      4.使用列别名改变查询结果的列标题
    • 选择表中的若干元组
      1.消除取值重复的行
      2.比较大小
      3.确定范围
      4.确定集合
      5.字符匹配
      6.涉及空值
      7.多重条件
    • ORDER BY子句
    • 聚集函数
    • GROUP BY子句
  • 连接查询
    • 等值与非等值连接查询 自然连接
    • 自身连接
    • 外连接(左外,右外)
    • 多表连接
  • 嵌套查询(相关子查询,不相关子查询)
    • 带有IN谓词的子查询
    • 带有比较运算符的子查询
    • 带有ANY(SOME)或ALL谓词的子查询
    • 带有EXISTS谓词的子查询
      1.实现全称量词
      2.实现逻辑蕴含
  • 集合查询
    • 并 UNION
    • 交 INTERSECT
    • 差 EXCEPT
  • 基于派生表的查询

终于写完了!!太坎坷了!昨天刮大风把我们村变压器给刮坏了hhhh唯一的电工喝醉了叫不醒,今天上午才来电,我用流量上了一节课好卡啊啊啊,现在离开了电还真是不太行。
这次的EXISTS确实有点不好理解,但是我理解了!!离散我还记得一点点!!好多学科之间都有联系,就感觉有点神奇诶。这个NOT EXISTS给我一种双重否定表示肯定的感觉,还有那个好几层的给我一种递归的感觉,当然只是感觉,还是有很大区别的!别的倒是挺好理解,最后总结查询的时候,我呼啦呼啦写的可长,才意识到我已经学了这么多了吗!!好有成就感!

发布了10 篇原创文章 · 获赞 15 · 访问量 4749

猜你喜欢

转载自blog.csdn.net/qq_44871112/article/details/104964575