通过一个小问题来学习SQL关联查询
原话题: 是关于一个left join的,没有技术难度,但不想清楚不一定能回答出正确答案来: TabA表有三个字段Id,Col1,Col2 且里面有一条数据1,1,2 TabB表有两个字段Id,Col1且里面有四条数据 问题: 如下语句会返回多少条数据? 在不写测试脚本的情况下 ,如果你
原话题:
是关于一个left join的,没有技术难度,但不想清楚不一定能回答出正确答案来:
TabA表有三个字段Id,Col1,Col2 且里面有一条数据1,1,2
TabB表有两个字段Id,Col1且里面有四条数据
问题:如下语句会返回多少条数据? 在不写测试脚本的情况下,如果你能在5分钟内准备回答出答案,且能说出些所以然来(及不是凭感觉猜出来的结果),那么请继续看后面的问题。
Select * from TabA a Left join TabB b1 on a.Col1=b1.Col1 Left join TabB b2 on a.Col2=b2.Col1
延深问题:
现在表A多增加一条数据2,3,4 ,此时再运行上面的语句会几条数据?如果你能在2分钟内回答出正常答案,那么请继续看后面的问题。
理论问题:我发现就上面这个问题不少人回答不正确,这其中也包括我自己。为什么如此简单的问题往往会回答错误,我认为可能有如下原因:
对于第一种情况的人,短时间内无法解决,只有通过自身的学习来补救,对于第二种情况的人就需要稍微学习一些基本的理论知识就够用,对于第三种情况的人是一个态度问题。
left join的概念
简单来讲就是以左表做为外层循环表,每条每条去内层表去查找匹配记录,如果找到就返回join好的值,如果没找到返回外层表的值,内层表统一赋值为null。这里之所以说成简单来讲,是因为我是拿嵌套循环的例子来分析,因为这比较容易让非SQL方面的程序员明白,毕竟对于.net程序员来讲编写双层或者多层循环的例子会很多。而对于hash匹配以及合并联接的应用场景在.net程序中相对较少,类似如下的双层循环。
foreach(var colA in tabA) { foreach(var colB in tabB) { if(colA==colB) { ...... } } }
这里需要注意下,上面说到的外层表的记录循环去内层表查找时,这里有个问题,看这条语句:
Select * from TabA a Left join TabB b1 on a.Col1=b1.Col1
这里的TabA 就是我这里讲的外层表,TabB就是内层表,外层表就一行数据,内层表有4行数据,从上面给出的数据来看,用来做等值判断的条件是外层表的Col1字段与内层表的Col1字段,拿外层表的Cole=1这行数据去内层表查询时,内层表的第一条数据符合条件,其它三条不符合,此时的结果会是下面的哪一种呢?
a.Id a.Col1 a.Col2 b.Id b.Col1
1 1 2 1 1
1 1 2 null null
1 1 2 null null
1 1 2 null null
a.Id a.Col1 a.Col2 b.Id b.Col1
1 1 1 2 1
这要理解当在内层表中找到数据以及找不到数据的区别,我们拿外层表Col1=1这条数据去内层表查找时,需要查找4次,其中有一条符合,三条不符合,这说明找到了匹配数据,所以只返回匹配的数据行,即一条数据,而不会出现上面的第一种结果返回4条数据。
这是我当时遇到这个问题时产生的误解。
再看后面的那个left join
Select * from TabA a Left join TabB b1 on a.Col1=b1.Col1 Left join TabB b2 on a.Col2=b2.Col1
容易产生的问题,再进行第二次left join 的时候,外层表是TabA原始表呢还是第一次left join 之后的结果集呢? 看下我列出来的表头,就很容易理解了,这里的a.Col2就是第一次left join后的结果集。( a.Id a.Col1 a.Col2 b.Id b.Col1)
我们可以做下测试,这里使用inner join来做测试,,因为这加容易比较出差异,运行下面的语句,此时TabA中有两条数据,就是上面延深问题中添加的2,3,4这条。
Select * from TabA a inner join TabB b1 on a.Col1=b1.Col1 inner join TabB b2 on a.Col2=b2.Col1
分两步来看:
Select * from TabA a inner join TabB b1 on a.Col1=b1.Col1
这里只会返回一条数据,因为inner join返回的交集。
a.Id a.Col1 a.Col2 b.Id b.Col1
1 1 2 1 1
如果第二次join 时,如果连接的是原始表TablA,那么循环查询的次数应该是TabA的总条数2,但从下面的执行计划图可以分析出执行顺序。