外连接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条件中,这样结果才能不多不少,刚刚好。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值