一、带有EXISTS谓词的子查询
①EXISTS代表存在量词∃。带有EXISTS的子查询不返回任何数据,只产生逻辑真值‘true’或逻辑假值‘false’。
②使用存在量词EXISTS后,若内层查询结果非空,则外层的WHERE子句返回真值,否则返回假值。
③与EXISTS谓词相对的是NOT EXISTS谓词,使用存在量词NOT EXISTS后,若内层查询结果空,则外层的WHERE子句返回真值,否则返回假值。
④由EXISTS引出的子查询,其目标列表达式通常都用*,因为带EXISTS的子查询只返回真值或假值,给出列名无实际意义。
⑤所有带IN谓词、比较运算符、ANY和ALL谓词的子查询都能用带EXISTS谓词的子查询等价替换。
(本例是相关子查询)
SELECT Sname
FROM Student
WHERE EXISTS
(SELECT*
FROM SC
WHERE Sno=Student.Sno AND Cno='1');
查询结果:
SELECT Sname
FROM Student
WHERE NOT EXISTS
(SELECT*
FROM SC
WHERE Sno=Student.Sno AND Cno='1');
查询结果:
SQL里没有全称量词,但是可以把带有全称量词的谓词转换为等价的带有存在量词的谓词。
所以该题的意思可以转换为:查询这样的学生,没有一门课是他不选修的。
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来实现带全称量词的查询。
本查询可以用逻辑蕴涵来表达:查询学号为x的学生,对所有的课程y,只要201215122学生选修了课程y,则x也选修了y。
形式化表示:用p表示谓词“学生201215122学生选修了课程y”
用q表示谓词“学生x选修了课程y”
则上述查询为:(∀y)p→q
SQL语言中没有蕴涵逻辑运算,但是可以利用谓词演算将一个逻辑蕴涵的谓词等价转换为:p→q ≡ ┐p∨q
该查询可以转换为如下形式:
(∀y)p→q≡ ┐∃y(p∧┐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));
二、集合查询
集合操作主要包括并操作UNION,交操作INTERSECT和差操作EXCEPT。
SELECT *
FROM Student
WHERE Sdept='CS'
UNION
SELECT *
FROM Student
WHERE Sage<=19;
查询结果:
使用UNION将多个查询结果并起来时,系统会自动去掉重复元组。如果要保留重复元组则用UNION ALL操作符
SELECT Sno
FROM SC
WHERE Cno='1'
UNION
SELECT Sno
FROM SC
WHERE Cno='2';
查询结果:
SELECT *
FROM Student
WHERE Sdept='CS'
INTERSECT
SELECT *
FROM Student
WHERE Sage<='19';
查询结果:
(没有什么错是不能出的😁,我第一个SELECT后面写的*,第二个我就写成了Sno,然后就出错了🐷自罚三瓶养乐多👍)
SELECT Sno
FROM SC
WHERE Cno='1'
INTERSECT
SELECT Sno
FROM SC
WHERE Cno='2';
查询结果:
(注意,这个题就不能SELECT *,否则会没有查询结果,因为上一题查询的是不同属性,而这个题查询的是同一个属性的不同结果,比如这个查询时如果不只选择学号,则会造成课程号和成绩不同则没有查询结果)
SELECT *
FROM Student
WHERE Sdept='CS'
EXCEPT
SELECT *
FROM Student
WHERE Sage<='19';
查询结果:
(也就是查询计算机科学系中年龄大于19岁的学生)
三、基于派生表的查询
子查询不仅可以出现在WHERE子句中,还可以出现在FROM子句中,这时子查询生成的临时派生表成为主查询的查询对象。
/*改写后的*/
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);
查询结果都为 :
Emmmm,相比较我更喜欢改写前的那种(因为好写)。
改写前的意思是:这就是一个嵌套查询,先查询SC x的学生,然后在y里求了这个学生所有选修的平均成绩后,在跟该学生的各科成绩比较,然后返回符合条件的查询值。
改写后的意思是:FROM子句中子查询会生成一个新表,主查询将SC表和新生成的派生表再通过某种属性相连接。
如果子查询中没有聚集函数,派生表可以不指定属性列,子查询SELECT后面的列名为其默认属性。比如下例:
/*改写后的*/
SELECT Sname
FROM Student,(SELECT Sno FROM SC WHERE Cno='1')AS SC1
WHERE Student.Sno=SC1.Sno;
/*改写前的*/
SELECT Sname
FROM Student
WHERE EXISTS
(SELECT*
FROM SC
WHERE Sno=Student.Sno AND Cno='1');
查询结果都是:
改写前的意思是:查询Student表里的学生,在查找SC表这些学生里Cno是否为1,是的话返回真值,返回该学生对应的Sname,不是的话查询下一个元组。
改写后的意思是:把SC表里Cno=1的形成一个派生表,在查询与Student表与SC表里Sno相等的,返回该学生的Sname。
通过FROM子句生成派生表时,AS关键字可以省略,但必须为派生关系指定一个别名。
SELECT总结:
SELECT语句的一般格式:
SELECT [ALL|DISTINCT] <目标列表达式> [别名] [ ,<目标列表达式> [别名]]…
FROM <表名或视图名> [别名] [ ,<表名或视图名> [别名]]… |(<SELECT语句>)[AS]<别名>
[WHERE <条件表达式>]
[GROUP BY <列名1>[HAVING<条件表达式>]]
[ORDER BY <列名2> [ASC|DESC]];
SELECT查询:
①单表查询:选择表中的若干列;选择表中的若干元组;ORDER BY子句;聚集函数;GROUP BY子句。
②连接查询:等值与非等值连接查询;自身连接;外连接;多表连接。
③嵌套查询:带IN谓词的子查询;带有比较运算符的子查询;带有ANY或ALL谓词的子查询;带有EXISTS谓词的子查询。
④集合查询:集合操作主要包括并操作UNION,交操作INTERSECT和差操作EXCEPT。
⑤基于派生表的查询