在王珊、萨师煊的《数据库系统概论》第五版书中的P108例子比较难理解。
存在量词
EXISTS谓词:
带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值“true”或逻辑假值“false”。
若内层查询结果非空,则外层的WHERE子句返回真值;
若内层查询结果为空,则外层的WHERE子句返回假值。
由EXISTS引出的子查询,其目标列表达式通常都用* ,因为带EXISTS的子查询只返回真值或假值,给出列名无实际意义。
NOT EXISTS谓词
若内层查询结果非空,则外层的WHERE子句返回假值;
若内层查询结果为空,则外层的WHERE子句返回真值。
SQL中没有全称量词,可以把带有全称量词的谓词转换为等价的带有存在量词的谓词。
【例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
)
);
理解这个代码,可以举反例。
对于一个学生,如果他有一门课程没有选,那么这个学生就不属于选修了全部课程的学生。
所以要找的学生是这样一些学生。
课表上的每一门课,如果有课程没有选,就把他归类到有课程没选的学生群体里。
而所要找的学生就是不在上面这个群体里的学生。
代码遍历学生表,对于每一个学生,在课程表中找,当该学生有课程没选时,内层NOT EXISTS为true,此时外层NOT EXISTS为false。只有所有课程都选了,内层才为false,此时外层就为true了,表明该学生所有的课都选了。
【例3.63】查询至少选修了学生201215122选修的全部课程的学生学号。
SELECT DISTINCT Sno
FROM SC SCX
WHERE NOT EXISTS
( SELECT *
FROM SC SCY
WHERE SCY.Sno = '20121522' AND NOT EXISTS
( SELECT *
FROM SC SCZ
WHERE SCZ.Sno = SCX.Sno AND SCZ.Cno = SCY.Cno
)
);
对于每个学生,不在这样的学生群体中。
该群体为对于学号为20121522的学生所修的每一门课程,只要有一门课,有个学生没有选,那么这个学生就被加到这个群体里,意味着第二个NOT EXISTS为true,此时第一个NOT EXISTS为false。