- 带有EXISTS谓词的子查询
- 集合查询
- 基于派生表的查询
- Select总结
学完查询啦!!嘻嘻嘻这周系统看一遍前边,写了博客真的蛮方便!那俺开始了。
带有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语言中没有全称量词
(For all)
可以把带有全称量词的谓词转换为等价的带有存在量词的谓词:
【例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语言中没有蕴涵逻辑运算
可以利用谓词演算将逻辑蕴涵谓词等价转换为:
【例3.63】查询至少选修了学生201215122选修的全部课程的学生号码。
用逻辑蕴涵表达:查询学号为x的学生,对所有的课程y,只要201215122学生选修了课程y,则x也选修了y。
形式化表示:(离散数学知识,我还记得!!!)
用p表示谓词 “学生201215122选修了课程y”
用q表示谓词 “学生x选修了课程y”
则上述查询为:
等价变换:
变换后语义:不存在这样的课程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给我一种双重否定表示肯定的感觉,还有那个好几层的给我一种递归的感觉,当然只是感觉,还是有很大区别的!别的倒是挺好理解,最后总结查询的时候,我呼啦呼啦写的可长,才意识到我已经学了这么多了吗!!好有成就感!