自己蒙着脑袋想百思却也不得其解,侥幸找到一些参考,特此记录分享。
参考书籍,原书封面置于文后
《数据库系统概论》萨师煊 P108,例3.62
《数据库系统概念》 P51
读懂的关键在于正确理解 not exists,是从集合的角度,而非单一记录。
我们先假定一个简单的场景:
sno为'1'的学生,在三门课程中,选修了cno为‘2’,‘3’的两门课程,单单没有选修cno ‘1’
sno cno
1 1 ×
1 2 √
1 3 √
《数据库系统概论》,P108
根据假定的场景,先从Student表中取第一条元组,将sno ‘1’送入内层查询块。由于sno‘1’与cno'1'的组合不在选课表SC中,内层查询块返还结果为空 null,即false。
where Not EXISTS false(单一布尔值,直接取反),相当于 where true。导致sno '1' 被筛选出。
我们用同样的思路来分析《数据库系统概论》萨师煊 P108 例3.62
这里涉及到三张表,Student(sno)、Course(cno)、SC(sno,cno),最内层的查询块,给出了这三张表的连接条件:where SC.sno = Student.sno and SC.cno = Course.cno; 是将选课表SC与学生表Student做笛卡尔积,得到sno的学生信息,将选课表SC与课程表Course作笛卡尔积,得到cno的课程信息。
同样根据假定的场景,从Student表中取第一条元组,sno ‘1’被送入内层查询块。我们理解的执行过程是,从Student表中取sno ‘1’,从Course表中取cno '1',送入最内层的查询块,在SC表中没有这样一条记录,where SC.sno = Student.sno and SC.cno = Course.cno 的结果肯定是 false
两次not exists,得到的结果仍然是false,导致sno '1'不被输出。与它没选修cno ‘1’事实相符。
为什么错,这就是在从单一记录的角度理解 not exists。代码最神奇的地方在于,几乎看不出有集合的痕迹,却实现了与全体课程作比较(在我自己的代码中,通过聚集函数count(*),将集合转化成了数字)。
将错进行到底,就能明白思路中确实存在着问题。
sno ‘1’被送了入内层查询块,继续从Course表中取cno '2',送入最内层的查询块,在SC表中有这样一条记录,where SC.sno = Student.sno and SC.cno = Course.cno 的结果为 true
两次not exists,得到的结果仍然是true,导致这时 sno '1'被输出。
同样的思路,sno '1' 和 cno '3'时,会导致sno '1'又一次被输出。
这里强调一下,要从集合的角度理解not exists。这行代码中的not exists一共出现了两次,它们针对的对象并不都是单一的布尔值。第二个not exists面对的场景比较单纯,最内层查询块处理的只是一个学号跟一个课程号,看看选课表里有没有,有就返还一个布尔值true,没有就给一个布尔值
false,第二个not exists我只要把这个布尔值取反就是逻辑正确。第一个not exists,是sno ‘1’ 的,等待它是三个布尔值。有内层查询块处理cno '1'得到的true,处理cno '2'和cno ‘3’得到的两个false。只有内层查询得到的全都是false的时候,才并成一个false,由第一个not exists取反得真,将该名学生输出。也就是该学号与每一门课程组合,去SC表里面找,每一个组合都能够找到,最内层查询块返还的结果都是true。所以机关在于,not exists对这些布尔值的运算应该是 b1 || b2 || b3,是或的关系,有一个真就是真。翻译翻译就是,在我的最内层查询块中,我从Course表中逐个拿cno,跟sno一起到SC表送检,一旦捕捉到有一个cno,SC表中没有,吐出一个false(即连接条件是where false,经内层查询块中的第二个not exists取反即得true),该学生得强行输出(true),它没有选修全部课程。只有全假才是假,即该生与每一门课的送检,都在SC表中找到,这才被丢掉,不进入输出名单。
辅助资料: 《数据库系统概念》原书 P51
关于全称命题和特称命题的补充
全称命题:对每一个,命题p(x)都成立。它的否定是:存在一个,使命题不成立。非,取反,两次取反得它本身。与或非,逻辑运算,也称布尔运算。
人教版高中数学选修2-1 P24
参考书籍封面