上次我只是把全联接的SQL语句写出来了,但是没有对其内在的过程做分析,只是简单提到了一下,内部做了四次表扫描。昨天抽空又看了一下,终于基本弄懂了里面的原理,首先我们来看一下查询计划给出的图:
右上方两次表扫描加一次嵌套循环是做的一次标准的左连接,在图中也明确的标示出来了。关键是右下方的那两次表扫描和连接。表扫描好说,就是为连接作准备的,那么,右下角的那次连接是怎么回事呢,而且从图上标示的信息可以看出,连接的类型是"Left Anti Semi Join ",一个从来都没有听说过的连接方式。网上google了一把,第一个找到的就是MS自己的站点,里面的说明如下:
当第二个(底端)输入中没有匹配行时,Left Anti Semi Join 运算符返回第一个(顶端)输入中的每一行。如果 Argument 列内不存在任何联接谓词,则每行都是一个匹配行。
怎么看都觉得有点绕口令的味道,而且连看了三遍愣是没明白是什么意思,最后根据查询的结果和前面分析的成果,再加上从词霸上查到的Semi的意思(半,不完全的),连蒙带猜的,总算是大概明白了一点。姑且妄言一下:这个Left Anti Semi Join就是一个内部的操作,有点私有函数的味道。作用就是把前面的左连接里左边表没有出现的行,而又出现在右边表里的行找出来,怎么看都有点右连接的味道。但这个semi,也就是不完全的,实在是不好理解,可能是算法优化后的结果吧。这也符合全连接的定义----把参加连接的左表和右表里各自没有的部分也都取到结果集中。剩下的串联就好理解了,就是union操作的意思。
回过头来想想因为存在两次相对独立的连接操作,可不是嘛要进行四次表扫描。
在这基础上我又想到了,里面那个半连接的作用既然那么像一个右联接,那么我再用一个右连接的操作来代替,然后自己再作union这一步,就是把内部执行的过程拿到外面来用SQL语句来替代。于就就又有了如下的SQL:
select T1.A, T1.B, T1.C, T1.D, T2.E
from T1
left join T2 on T1.A=T2.A and T1.B=T2.B and T1.C=T2.C
union
select T1.A, T1.B, T1.C, T1.D, T2.E
from T2
left join T1 on T1.A=T2.A and T1.B=T2.B and T1.C=T2.C
上一章提到了,所有的右连接都可以转换为一个相等的左连接,这样得到的查询结果也完全正确。下面是这个查询的查询计划的图表:
从这个图来看,与前一张图极为相似,都有四次表扫描,都有两次连接,都有一个串联操作。第二张图里多了一个排序的步骤,那是因为显示的union操作是要先排序的,目的是排除重复的行。两种方式的比较结果,我个人看来前一种性能可能更好点,再怎么说半连接至少不会比一个完整的左连接的开销大吧,而且在串联后还少了排序这么个开销占了47%的过程。
一个多表查询引出的问题到此就告一个段落,基本上能想到的查询方式也都出现在这里了,以后再有什么新的发现我会接着补充在后面的。