外连接left join 过滤条件选择是放on条件中还是where条件中

Posted 是Cc哈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了外连接left join 过滤条件选择是放on条件中还是where条件中相关的知识,希望对你有一定的参考价值。

一、问题描述
今天在使用left join进行连接查询的时候,由于过滤条件写错了导致查询的结果与自己预期想要达到的结果不一致,在这里通过一个简单的demo记录一下。

二、demo
现在有两张表,一张班级表,一张学生表。如下图所示:


需求一:找出每个班级的名称及其对应的⼥同学数量
需求二:找出⼀班的同学总数

三、错误的sql查询
对于需求一我不假思索的写出了如下sql语句:

SELECT c.name,  count(s.name)  as  num
FROM classes c  left  join students s
on s.class_id = c.id
where s.gender = 'F'
group  by c.name

自己感觉没什么问题,然而执行结果却是这个样子:

可以明显看出四班的女生数量是0,然而在查询结果中却并没有显示。

四、问题原因
mysql 对于left join的采⽤类似嵌套循环的⽅式来进⾏从处理,以下⾯的语句为例:
SELECT * FROM LT LEFT JOIN RT ON P1(LT,RT)) WHERE P2(LT,RT)
其中P1是on过滤条件,缺失则认为是TRUE,P2是where过滤条件,缺失也认为是TRUE,该语句的执⾏逻辑可以描述为:

FOR each row lt in LT  // 遍历左表的每⼀⾏
 BOOL b =  FALSE;
 FOR each row rt in RT such that P1(lt, rt)  // 遍历右表每⼀⾏,找到满⾜join条件的⾏
  IF P2(lt, rt)  //满⾜ where 过滤条件
   t:=lt||rt; //合并⾏,输出该⾏
  
  b= TRUE; // lt在RT中有对应的⾏

IF (!b)   // 遍历完RT,发现lt在RT中没有有对应的⾏,则尝试⽤null补⼀⾏
  IF P2(lt, NULL)  // 补上null后满⾜ where 过滤条件
    t:=lt|| NULL;  // 输出lt和null补上的⾏
    
  

从这个伪代码中,我们可以看出两点:
如果想对右表进⾏限制,则⼀定要在on条件中进⾏,若在where中进⾏则可能导致数据缺失,导致左表在右表中⽆匹配⾏的⾏在最终结果中不出现,违背了我们对left join的理解。因为对左表⽆右表匹配⾏的⾏⽽⾔,遍历右表后b=FALSE,所以会尝试⽤NULL补⻬右表,但是此时我们的P2对右表⾏进⾏了限制,NULL若不满⾜P2(NULL⼀般都不会满⾜限制条件,除⾮IS NULL这种),则不会加⼊最终的结果中,导致结果缺失。
如果没有where条件,⽆论on条件对左表进⾏怎样的限制,左表的每⼀⾏都⾄少会有⼀⾏的合成结果,对左表⾏⽽⾔,若右表若没有对应的⾏,则右表遍历结束后b=FALSE,会⽤⼀⾏NULL来⽣成数据,⽽这个数据是多余的。所以对左表进⾏过滤必须⽤where。

五、解决办法
所以,对于需求一由于在where条件中对右表限制,导致数据缺失。

修改后的sql语句为:

SELECT c.id, c.name,  count(s.name)  as  num
FROM classes c  left  join students s
on s.class_id = c.id
and s.gender = 'F'
group  by c.id

查询结果为:

数据缺失的问题解决!

同理,对于需求二,由于需要对左表进行限制,所以我们的过滤条件应该在where中,正确sql及结果如下:

SELECT c.name,  count(s.name)  as  num
FROM classes c  left  join students s
on s.class_id = c.id
where c.name = '一班'
group  by c.name


如果在on条件中会对左表限制,导致数据多余,错误sql及结果如下:

SELECT c.name,  count(s.name)  as  num
FROM classes c  left  join students s
on s.class_id = c.id
and c.name = '一班'
group  by c.name


六、总结
通过上⾯的问题现象和分析,可以得出了结论:在left join语句中,左表过滤必须放where条件中,右表过滤必须放on条件中,这样结果才能不多不少,刚刚好。

以上是关于外连接left join 过滤条件选择是放on条件中还是where条件中的主要内容,如果未能解决你的问题,请参考以下文章

Oracle的left join中on和where的区别

SQL——左连接(Left join)右连接(Right join)内连接(Inner join)

多表查询-inner join left join right joinfull join

mysql left join中where和on条件的区别

sql中在使用left join 时 on and 和on where 的区别

left join 和 on 的区别