带有EXISTS谓词的子查询
EXISTS代表存在量词 ∃ \exists ∃。
带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值“true”或逻辑假值“false”。
使用存在量词EXISTS后,若内层查询结果非空,则外层的WHERE子句返回真值,否则返回假值。
与EXISTS谓词相对应的是NOT EXISTS谓词,使用存在量词NOT EXISTS后,若内层查询结果为空,则外层的WHERE子句返回真值,否则返回假值。
【例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');
由EXISTS引出的子查询,其目标列表达式通常都用 *,因为带EXISTS的子查询只返回真值或假值,给出列名无实际意义。
子查询的查询条件依赖于外层父查询的某个属性值(Student.Sno),因此是相关子查询。处理过程是:首先取外层查询中的Student表的第一个元组,根据它与内层查询相关的属性值(Sno值)处理内层查询,若WHERE子句返回值为真,则取外层查询中该元组的Sname放入结果表;然后取Student表的下一个元组;重复这一过程,直至外层Student表全部检查完为止。
本例查询也可以用连接运算来实现
SELECT Sname
FROM Student,SC
WHERE SC.Sno=Student.Sno AND Cno='1';
【例3.61】查询没有选修1号课程的学生姓名。
与上一题相比就是在EXISTS前面加了一个NOT。
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谓词的子查询替换
/*第四种解法*/
SELECT Sno,Sname,Sdept
FROM Student S1
WHERE EXISTS
(SELECT *
FROM Student S2
WHERE S1.Sdept=S2.Sdept AND S2.Sname='刘晨');
在S1表取一个学生,让这个人的系别与刘晨的系别比较,相同就为真返回这个学生的学号姓名系别,然后取下一个;不为真就直接取下一个;直至表取完。
由于带EXISTS量词的相关子查询只关心内层查询是否有返回值,并不需要查具体值,因此其效率并不一定低于不相关子查询,有时是高效的方法。
用EXISTS/NOT EXISTS实现全称量词 难点
SQL语句中没有全称量词 ∀ \forall ∀,可以把带有全称量词的谓词转换为等价的带有存在量词的谓词: ( ∀ x ) P ≡ ¬ ( ∃ x ( ¬ P ) ) (\forall x)P \equiv \neg (\exists x(\neg P)) (∀x)P≡¬(∃x(¬P)