小明被大明将了一军,于是心里开始合计起来,假如是外连接,可能会对某一方补NULL值,这样的话TEACHER.tno = COURSE.tno这样的约束条件就无法构成等价类了啊,然后小在电脑上默默的敲了一个SQL语句:
1 | SELECT sname FROM TEACHER t LEFT JOIN COURSE c ON t.tno = 5 AND t.tno = c.tno; |
然后小明发现不但等价类可能产生不了了,而且选择下推也无法进行了,于是说:“这个语句中的TEACHER.tno=5不能下推了,因为左连接的语义是外表的所有的数据都要输出出来,如果把TEACHER.tno=5下推到TEACHER表上,那就会在做左连接之前先对TEACHER表做过滤,导致查询结果的不等价,而且由于补NULL值,等价类也生成不了了。”
大明说:“对的,小明同学你理解的很快,由于外连接补NULL值的关系,确实导致无法做谓词下推,不过你可以看一下下面的这个语句,看看有什么区别。”然后大明在电脑里输入了另一个类似的SQL语句:
1 | SELECT sname FROM TEACHER t LEFT JOIN COURSE c ON TRUE WHERE t.tno = 5 AND t.tno = c.tno; |
小明仔细的观察上面的例句和当前这个例句,发现约束条件一个处在ON后面,另一个是处在WHERE后面,小明好像还不是很理解他们的含义,于是向大明投去了咨询的目光。大明说:“我们粗略的分一下,ON后面的约束条件是连接条件,WHERE后面的约束条件是过滤条件,连接条件和过滤条件是不同的。”
小明好像悟到了什么,抢着说:“我知道了,一个是连接中的,一个是连接后的,可以这么理解吧?你看,连接条件会参与到连接操作的过程中,满足连接条件的会显示出来,不满足连接条件的,根据连接的类型还会决定是否补NULL值,而过滤条件是在连接操作之后对连接的结果进行过滤。。。”小明又对大明投去了期待的眼神,这种眼神是期望得到一点肯定,大明笑着说:“对,可以这么理解。”
小明赶紧说:“你先别讲,让我看看这个带有过滤条件的SQL语句是不是能下推。”,然后小明对着这个SQL语句仔细观察起来,口中念念有词,似乎是已经走火入魔了。
小明观察了半天,好无收获,只好说:“我掐指一算,觉得这个约束条件是能下推的,而且等价类也能建立起来。”
“为什么?”大明追问道。
“不为什么,我脑海中灵光乍现,我是一个佛系的程序员。”
“你果然没有人生的字典,想必你即使有人生字典,你也把羞耻二字给扣掉了。”
小明笑着说:“是的,像我这样优秀的人,本该灿烂过一生啊。。。”
大明看小明竟然唱起了歌,愤怒的说:“不要胡闹,再胡闹我就让你一杯敬自由,一杯敬死亡。。。”大明停顿了一下,继续说道:“实际上这里还有一个严格的概念,什么叫严格呢?一个表达式,如果它的输入是NULL并且输出也是NULL,那么我们就说这个表达式是严格的,另外我们可以扩展一下严格的定义,从而定义出一个叫做‘宽泛的严格’的概念,就是说如果一个表达式它的输入是NULL,它的输出是NULL或者false,那么我们就说它符合宽泛的严格。”
“那么严格有什么用呢?”
“如果对一个元组应用约束条件,如果约束条件求值返回的是NULL值或者FALSE,实际上代表的是这一条元组不输出,明白吗?”
“哦,那就是说我们补的NULL值如果遇到这种过滤条件,就不会输出出来喽。”小明停了一下,突然想到了些什么,继续问道,“那这种外连接还补NULL值干嘛,岂不是没有什么卵用了?”
“对头,这就是外连接消除的基本原理,遇上这种严格的约束条件,外连接补的NULL值没有什么用,那也就转变成内连接就好了。问题来了,如果变成了内连接,我们又能肆意妄为的选择下推、没羞没臊的做等价推理了,惊不惊喜,意不意外?”
“哈哈,那你能给我一个不严格的例子不?让我见识一下不严格的表达式。”
“比如说IS NOT NULL,输入是NULL值,输出竟然是TRUE,还有COALESCE函数,输入是NULL值,输出是啥随你定。”
小明说:“看来表达式是否严格是很重要的一个概念,通过这个概念我们能做外连接消除,外连接消除又能够导致选择能够下推。。。这我就明白了为什么要做外连接消除了。”
“嗯,外连接消除不只是将外连接转换成内连接,其实还有一种情况,它也和我们要说的表达式的条件是否严格有关,那就是可以将外连接转换成AntiJoin,我们来看这样一个例子。”
1 | SELECT * FROM STUDENT LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno WHERE SCORE.sno IS NULL; |
“由于STUDENT.sno = SCORE.sno是严格的,而且是连接条件,这样在连接的过程中他会将在STUDENT表和SCORE表中原有的NULL值去除掉,反而由于SOCRE.sno IS NULL是过滤条件,它起到了过滤作用,会将外连接补充NULL值的数据全部保留下来,这个语句的执行结果实际上就相当于做了一个Anti Join,因此这个语句可以转换成为Anti Join,它转换的结果相当于下面这个语句。”大明在电脑上敲出了等价语句,啪啪啪。。。
1 | SELECT * FROM STUDENT ANTI JOIN SCORE ON STUDENT.sno = SCORE.sno |
“不过需要注意,SQL语法中是没有ANTI JOIN的,这只是一个等价的语句,但是它无法直接执行。另外在外连接消除的阶段还有一个“很重大”的举措,就是把左外连接全部转换成了右外连接,这样就可以在后续的代码中少处理一种情况,简化了后面的代码逻辑。”
“严格果然是太有用了,可是我怎么知道一个表达式是不是严格呢?”
“对于函数而言,在PG_PROC系统表中的proisstrict列属性代表了当前函数是否严格,如果是操作符表达式,在PostgreSQL数据库中操作符实际都转成了对应的函数,因此也可以用proisstrict来表示是否严格,而对基于IS [NOT] NULL产生的NullTest表达式需要单独处理,其中IS NOT NULL是严格的,IS NULL是不严格的,大体上我们可以分成这么几类。”
小明感觉自己今天好像学到了好多知识,幸好自己有个哥哥,不然很难有人这么教自己,于是发自内心的说:“OK,我又增加了新的知识,谢谢。”
“不要谢我,我是雷锋,我们都是在为中华之崛起而读书,想想我们可是社会主义接班人啊。。。”大明感觉自己胸前的仿佛有红领巾在迎风飘扬,而且更加鲜艳了。