背景

left join在我们使用mysql查询的过程中可谓非常常见,比如博客里一篇文章有多少条评论、商城里一个货物有多少评论、一条评论有多少个赞等等。但是由于对join、on、where等关键字的不熟悉,有时候会导致查询结果与预期不符,所以今天我就来总结一下,一起避坑。

这里我先给出一个场景,并抛出两个问题,如果你都能答对那这篇文章就不用看了。

假设有一个班级管理应用,有一个表classes,存了所有的班级;有一个表students,存了所有的学生,具体数据如下(在线SQL:https://www.liaoxuefeng.com/wiki/1177760294764384/1179611432985088):

SELECT * FROM classes;
id name
1 一班
2 二班
3 三班
4 四班

SELECT * FROM students;
id class_id name gender
1 1 小明 M
2 1 小红 F
3 1 小军 M
4 1 小米 F
5 2 小白 F
6 2 小兵 M
7 2 小林 M
8 3 小新 F
9 3 小王 M
10 3 小丽 F

那么现在有两个需求:
1、找出每个班级的名称及其对应的女同学数量
2、找出一班的同学总数

对于需求1,大多数人不假思索就能想出如下两种sql写法,请问哪种是对的?

SELECT 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.name
或者
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

对于需求2,大多数人也可以不假思索的想出如下两种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
或者
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
请不要继续往下翻 !!先给出你自己的答案,正确答案就在下面。
.
.
.
.
.
.
.
.
答案是两个需求都是第一条语句是正确的,要搞清楚这个问题,就得明白mysql对于left join的执行原理,下节进行展开。

原理

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补上的行
}
}
}
当然,实际情况中MySQL会使用buffer的方式进行优化,减少行比较次数,不过这不影响关键的执行流程,不在本文讨论范围之内。

从这个伪代码中,我们可以看出两点:
1、右表限制用ON
如果想对右表进行限制,则一定要在on条件中进行,若在where中进行则可能导致数据缺失,导致左表在右表中无匹配行的行在最终结果中不出现,违背了我们对left join的理解。因为对左表无右表匹配行的行而言,遍历右表后b=FALSE,所以会尝试用NULL补齐右表,但是此时我们的P2对右表行进行了限制,NULL若不满足P2(NULL一般都不会满足限制条件,除非IS NULL这种),则不会加入最终的结果中,导致结果缺失。
2、左表限制用WHERE
如果没有where条件,无论on条件对左表进行怎样的限制,左表的每一行都至少会有一行的合成结果,对左表行而言,若右表若没有对应的行,则右表遍历结束后b=FALSE,会用一行NULL来生成数据,而这个数据是多余的。所以对左表进行过滤必须用where。

分析总结

下面展开两个需求的错误语句的执行结果和错误原因:

需求1
name num
一班 2
二班 1
三班 2
需求1由于在where条件中对右表限制,导致数据缺失(四班应该有个为0的结果)

需求2
name num
一班 4
二班 0
三班 0
四班 0
需求2由于在on条件中对左表限制,导致数据多余(其他班的结果也出来了,还是错的)

通过上面的问题现象和分析,可以得出了结论:
在left join语句中,左表过滤必须放where条件中,右表过滤必须放on条件中

SQL 看似简单,其实也有很多细节原理在里面,一个小小的混淆就会造成结果与预期不符,所以平时要注意这些细节原理,避免关键时候出错。

转自:https://segmentfault.com/a/1190000020458807

MySQL之LEFT JOIN中使用ON和WHRERE对表数据的更多相关文章

  1. MySQL的left join中on与where的区别

    关于 “A LEFT JOIN B ON 条件表达式” 的一点提醒 ON 条件(“A LEFT JOIN B ON 条件表达式”中的ON)用来决定如何从 B 表中检索数据行,即使on中包含有A表中的列 ...

  2. mysql提取.sql备份文件中的单个表以及表数据

    背景:随着业务模块的不断在增多,数据库mysql容量也是越来越大,做测试时,整个备份还原比较耗费时间,由于有时候仅仅需要单个表或者少数几个表,要想从整个备份文件中提取指定的表以及数据,需要以下方法. ...

  3. Mysql Join-连接查询(中)

    Mysql Join-连接查询(中) 认识 就我平时的数据接触来看, 连接查询也没有很复杂,不够是非常需要耐心和逻辑的, 一点点将数据查出来, 拼接等. 没有什么技巧, 多练习就会了. 无非就是表之间 ...

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

    需要知道sql中关键字的执行顺序. FROM-> ON->JOIN-> WHERE->GROUP BY-> HAVING->SELECT-> DISTINCT ...

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

    left join中关于where和on条件的几个知识点: 1.多表left join是会生成一张临时表,并返回给用户 2.where条件是针对最后生成的这张临时表进行过滤,过滤掉不符合where条件 ...

  6. mysql left join中on后加条件判断和where中加条件的区别

    left join中关于where和on条件的几个知识点: .多表left join是会生成一张临时表,并返回给用户 .where条件是针对最后生成的这张临时表进行过滤,过滤掉不符合where条件的记 ...

  7. 转: 从Mysql某一表中随机读取n条数据的SQL查询语句

    若要在i ≤ R ≤ j 这个范围得到一个随机整数R ,需要用到表达式 FLOOR(i + RAND() * (j – i + 1)).例如, 若要在7 到 12 的范围(包括7和12)内得到一个随机 ...

  8. Mysql子查询IN中使用LIMIT

    学习下Mysql子查询IN中使用LIMIT的方法. 这两天项目里出了一个问题,mysql LIMIT使用后报错. 需求是这样的,我有3张表,infor信息表,mconfig物料配置表,maaply物料 ...

  9. mysql关键字讲解(join 、order by、group by、having、distinct)

    1.join     1.1 OUTER JOIN:想要包含右侧表中的所有行,以及左侧表中有匹配记录的行.        1.11 Mysql中有左连接(left join):            ...

随机推荐

  1. 将自己写的组件封装成类似element-ui一样的库,可以cdn引入

    在写好自己的组件之后 第一步 修改目录结构 在根目录下创建package文件夹,用于存放你要封装的组件 第二部 在webpack配置中加入 pages与publicpath同级 pages: { in ...

  2. MySQL安装到最后一步未响应MySQL Server Instance Configuration Wizard

    MySQL安装到最后一步未响应 第一个方法: 打开C盘,并且显示隐藏文件,然后在C盘下就能找到一个文件夹叫  "ProgamData"  ,打开它,删除里面的  "mys ...

  3. 27-SQLServer系统扩展存储过程

    一.注意点 1.在SQLServer中,有些系统扩展存储过程,是有风险,需要取消public角色的执行权限. 2.从SQLServer2005开始就不能通过sp_dropextendedproc 删除 ...

  4. Oracle 中文转字母 函数

    CREATE OR REPLACE FUNCTION F_TRANS_PINYIN_CAPITAL(P_NAME IN VARCHAR2) RETURN VARCHAR2 AS V_COMPARE V ...

  5. 解决每次执行Java等命令时都要重新source /etc/profile后才能执行,否则找不到命令

    linux mint 我们通常将环境变量设置在/etc/profile这个文件中,这个文件是全局的. /etc/profile:在登录时,操作系 统定制用户环境时使用的第一个文件 ,此文件为系统的每个 ...

  6. ES6-21.class基本语法

    1.简介(详情参考) class是构造函数的语法糖. class的constructor方法内的实现,就是原来构造函数的实现. class内的所有方法都是在prototype上的,就是原来构造函数的p ...

  7. ** WARNING ** : Your ApplicationContext is unlikely to start due to a @ComponentScan of the default package.

    https://blog.csdn.net/qq_15071263/article/details/78459087 1. 警告解读 ** WARNING ** : Your ApplicationC ...

  8. APIO2009 抢掠计划 Tarjan DAG-DP

    APIO2009 抢掠计划 Tarjan spfa/DAG-DP 题面 一道\(Tarjan\)缩点水题.因为可以反复经过节点,所以把一个联通快中的所有路口看做一个整体,缩点后直接跑\(spfa\)或 ...

  9. c 判断是否为nan

    /* isnan example */ #include <stdio.h> /* printf */ #include <math.h> /* isnan, sqrt */ ...

  10. c++ 容器反转并且拷贝到一个新容器中

    // reverse_copy example #include <iostream> // cout #include <algorithm> // reverse_copy ...