通俗来讲,left join就是以左表作为主表,结果返回左表的所有记录,右表满足条件记录正常显示,满足条件记录使用NULL做填充,一般业务中我们需要显示左表全部记录时才会使用left join。另外,某些情况下MySQL优化器会将我们的left join改写为join,什么情况下MySQL会做这样的优化?

1.1 测试数据

root@mysql 11:24:  [db1]> show create table t1\G
*************************** 1. row ***************************Table: t1
Create Table: CREATE TABLE `t1` (`uid` int(11) NOT NULL AUTO_INCREMENT,`uname` varchar(10) DEFAULT NULL,`is_delete` int(11) DEFAULT '0',PRIMARY KEY (`uid`),KEY `idx_uname` (`uname`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)root@mysql 11:24:  [db1]> show create table t2\G
*************************** 1. row ***************************Table: t2
Create Table: CREATE TABLE `t2` (`id` int(11) NOT NULL AUTO_INCREMENT,`uname` varchar(10) DEFAULT NULL,`uage` varchar(10) DEFAULT NULL,PRIMARY KEY (`id`),KEY `idx_uname` (`uname`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)root@mysql 11:24:  [db1]> select * from t1;
+-----+-------+-----------+
| uid | uname | is_delete |
+-----+-------+-----------+
|   1 | aa    |         0 |
|   2 | bb    |         0 |
|   3 | cc    |         0 |
|   4 | dd    |         0 |
|   5 | ee    |         0 |
+-----+-------+-----------+
5 rows in set (0.00 sec)root@mysql 11:24:  [db1]> select * from t2;
+----+-------+------+
| id | uname | uage |
+----+-------+------+
|  1 | aa    | 12   |
|  2 | aa    | 13   |
|  5 | cc    | 12   |
+----+-------+------+
3 rows in set (0.00 sec)

1.2 条件放在on和where之前的区别

1、从结果显示来看

从结果显示来看两者是完全不同的:

1)当把过滤条件写在and上时,返回结果集中会显示左表全部记录,右表满足条件的记录正常显示,不满足条件的记录显示为NULL,是我们通俗理解上left join应该显示的结果;

2)而当把过滤条件写在where上时,虽然SQL中我们使用了left join去做表关联,但是实际结果集中并不是我们想要的返回,结果只是返回了满足条件的所有记录。


root@mysql 11:24:  [db1]>  select * from t1 left join t2 on t1.uname=t2.uname and t2.uage>12;
+-----+-------+-----------+------+-------+------+
| uid | uname | is_delete | id   | uname | uage |
+-----+-------+-----------+------+-------+------+
|   1 | aa    |         0 |    2 | aa    | 13   |
|   2 | bb    |         0 | NULL | NULL  | NULL |
|   3 | cc    |         0 | NULL | NULL  | NULL |
|   4 | dd    |         0 | NULL | NULL  | NULL |
|   5 | ee    |         0 | NULL | NULL  | NULL |
+-----+-------+-----------+------+-------+------+
5 rows in set (0.00 sec)root@mysql 11:25:  [db1]>  select * from t1 left join t2 on t1.uname=t2.uname where t2.uage>12;
+-----+-------+-----------+------+-------+------+
| uid | uname | is_delete | id   | uname | uage |
+-----+-------+-----------+------+-------+------+
|   1 | aa    |         0 |    2 | aa    | 13   |
+-----+-------+-----------+------+-------+------+
1 row in set (0.00 sec)

2、从执行计划来看

1)当把过滤条件写在and上时,执行计划没有做过多的改写,左表t1作为驱动表与t2进行关联查询;

2)当把过滤条件写在where上时,我们发现MySQL对原SQL进行了改写,最重要的一点是将left join改写为join,这个动作就导致SQL在执行计划中会优先选择小表作为驱动表,而并不一定是左表t1作为驱动表。

root@mysql 11:26:  [db1]> explain  select * from t1 left join t2 on t1.uname=t2.uname and t2.uage>12;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                              |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    5 |   100.00 | NULL                                               |
|  1 | SIMPLE      | t2    | NULL       | ALL  | idx_uname     | NULL | NULL    | NULL |    3 |   100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)root@mysql 11:26:  [db1]> show warnings\G
*************************** 1. row ***************************Level: NoteCode: 1003
Message: /* select#1 */ select `db1`.`t1`.`uid` AS `uid`,`db1`.`t1`.`uname` AS `uname`,`db1`.`t1`.`is_delete` AS `is_delete`,`db1`.`t2`.`id` AS `id`,`db1`.`t2`.`uname` AS `uname`,`db1`.`t2`.`uage` AS `uage` from `db1`.`t1` left join `db1`.`t2` on(((`db1`.`t2`.`uname` = `db1`.`t1`.`uname`) and (`db1`.`t2`.`uage` > 12))) where 1
1 row in set (0.00 sec)root@mysql 11:26:  [db1]>
root@mysql 11:26:  [db1]>
root@mysql 11:26:  [db1]> explain  select * from t1 left join t2 on t1.uname=t2.uname where t2.uage>12;
+----+-------------+-------+------------+------+---------------+-----------+---------+--------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key       | key_len | ref          | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+-----------+---------+--------------+------+----------+-------------+
|  1 | SIMPLE      | t2    | NULL       | ALL  | idx_uname     | NULL      | NULL    | NULL         |    3 |    33.33 | Using where |
|  1 | SIMPLE      | t1    | NULL       | ref  | idx_uname     | idx_uname | 43      | db1.t2.uname |    1 |   100.00 | NULL        |
+----+-------------+-------+------------+------+---------------+-----------+---------+--------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)root@mysql 11:26:  [db1]> show warnings\G
*************************** 1. row ***************************Level: NoteCode: 1003
Message: /* select#1 */ select `db1`.`t1`.`uid` AS `uid`,`db1`.`t1`.`uname` AS `uname`,`db1`.`t1`.`is_delete` AS `is_delete`,`db1`.`t2`.`id` AS `id`,`db1`.`t2`.`uname` AS `uname`,`db1`.`t2`.`uage` AS `uage` from `db1`.`t1` join `db1`.`t2` where ((`db1`.`t1`.`uname` = `db1`.`t2`.`uname`) and (`db1`.`t2`.`uage` > 12))
1 row in set (0.00 sec)

1.3 left join … where … is null 的使用

基于以上的案例,我们可以得出以下结论:

1)left join会返回左表所有记录,右表满足过滤条件记录正常反馈,不满足记录返回NULL处理,join只会返回两表均满足过滤条件的记录

2)left join把过滤条件写在on条件上时才是我们通俗理解上的left join,而left join中将表过滤条件写在where上时,MySQL会把left join改写为join。

3)left join关联查询时,表的驱动顺序是确定的,左表作为驱动表与右表进行关联查询,但是若MySQL优化器将left join改写为join的情况下,MySQL就会优先选择小表作为驱动表进行关联查询,一定程度上提升了SQL的执行效率。

但是,对于第一/二条,将过滤条件放在where条件上的时候,MySQL优化器就一定会将left join 改写为join吗?left join一定会返回左表全部记录吗?答案显然是“不一定的”,以下就为大家展示一个特例。

root@mysql 11:42:  [db1]> select * from t1 left join t2 on t1.uname=t2.uname;
+-----+-------+-----------+------+-------+------+
| uid | uname | is_delete | id   | uname | uage |
+-----+-------+-----------+------+-------+------+
|   1 | aa    |         0 |    1 | aa    | 12   |
|   1 | aa    |         0 |    2 | aa    | 13   |
|   3 | cc    |         0 |    5 | cc    | 12   |
|   2 | bb    |         0 | NULL | NULL  | NULL |
|   4 | dd    |         0 | NULL | NULL  | NULL |
|   5 | ee    |         0 | NULL | NULL  | NULL |
+-----+-------+-----------+------+-------+------+
6 rows in set (0.00 sec)root@mysql 11:41:  [db1]> explain  select * from t1 left join t2 on t1.uname=t2.uname where t2.uage is null;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                              |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    5 |   100.00 | NULL                                               |
|  1 | SIMPLE      | t2    | NULL       | ALL  | idx_uname     | NULL | NULL    | NULL |    3 |    33.33 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)root@mysql 11:41:  [db1]> show warnings\G
*************************** 1. row ***************************Level: NoteCode: 1003
Message: /* select#1 */ select `db1`.`t1`.`uid` AS `uid`,`db1`.`t1`.`uname` AS `uname`,`db1`.`t1`.`is_delete` AS `is_delete`,`db1`.`t2`.`id` AS `id`,`db1`.`t2`.`uname` AS `uname`,`db1`.`t2`.`uage` AS `uage` from `db1`.`t1` left join `db1`.`t2` on((`db1`.`t2`.`uname` = `db1`.`t1`.`uname`)) where isnull(`db1`.`t2`.`uage`)
1 row in set (0.00 sec)root@mysql 11:41:  [db1]> select * from t1 left join t2 on t1.uname=t2.uname where t2.uage is null;
+-----+-------+-----------+------+-------+------+
| uid | uname | is_delete | id   | uname | uage |
+-----+-------+-----------+------+-------+------+
|   2 | bb    |         0 | NULL | NULL  | NULL |
|   4 | dd    |         0 | NULL | NULL  | NULL |
|   5 | ee    |         0 | NULL | NULL  | NULL |
+-----+-------+-----------+------+-------+------+
3 rows in set (0.00 sec)

可以看到,我们使用left join,但是where 过滤条件是右表某些字段is null的查询时。首先从执行计划来看,MySQL优化器并没有将left join改写为join;然后从结果返回来看,可以看到该SQL仅仅返回了满足过滤条件的记录,并没有返回左表全部记录。

对于left join,过滤条件是右表某字段is null的情况是一个特例,而且这种写法经常被DBA同学用来做业务上一些not in/not exists的改写优化。

1.4 结论

1)left join会返回左表所有记录,右表满足过滤条件记录正常反馈,不满足记录返回NULL处理,join只会返回两表均满足过滤条件的记录

2)left join把过滤条件写在on条件上时才是我们通俗理解上的left join,而left join中将表过滤条件写在where上时,MySQL会把left join改写为join。

3)left join且where过滤条件为右表某字段is null时属于一个特例,该情况下MySQL不会将left join改写为join,从而其表关联查询的顺序也只能是左表作为驱动表与右表进行关联查询。

4)left join关联查询时,表的驱动顺序是确定的,左表作为驱动表与右表进行关联查询,但是若MySQL优化器将left join改写为join的情况下,MySQL就会优先选择小表作为驱动表进行关联查询,一定程度上提升了SQL的执行效率。

正确理解left join相关推荐

  1. 正确理解线程WAITING状态

    正确理解线程WAITING状态 今天来学习下,Java的线程状态,重点讨论下thread.state.WAITING.讨论下线程如何进入此状态,以及它们之间的区别.最后,我们进一步了解java.uti ...

  2. Java 并发编程解析 | 如何正确理解Java领域中的锁机制,我们一般需要掌握哪些理论知识?

    苍穹之边,浩瀚之挚,眰恦之美: 悟心悟性,善始善终,惟善惟道! -- 朝槿<朝槿兮年说> 写在开头 提起Java领域中的锁,是否有种"道不尽红尘奢恋,诉不完人间恩怨"的 ...

  3. ADSL的PPPOE拨号客户端上的ppp authentication pap “callin”的正确理解

    ADSL的PPPOE拨号客户端上的ppp authentication pap "callin"的正确理解 对callin参数的理解一定要注意,很容易单让初学者通过"中国 ...

  4. 调整模型 与 提纯样本的关系过程有点类似EM算法过程,不知道这样理解是否是正确理解,固定A调B,B调到最优后,固定B再调A,循环往复,直至最优。

    调整模型 与 提纯样本的关系过程有点类似EM算法过程,不知道这样理解是否是正确理解,固定A调B,B调到最优后,固定B再调A,循环往复,直至最优.  个人理解

  5. html的区域大小,JavaScript位置与大小(1)之正确理解和运用与尺寸大小相关的DOM属性...

    在web开发中,不可避免遇到要计算元素大小以及位置的问题,解决这类问题的方法是利用DOM提供的一些API结合兼容性处理来,所有内容大概分3篇左右的文章的来说明.本文作为第一篇,介绍DOM提供的与尺寸大 ...

  6. 智能的定义是什么?如何正确理解智能家居?

    近几年,智能家居是一个比较火爆的行业!有很多的人(特别是寻找好的创业的项目的朋友)想从事这个行业,也有很多是想在自己家里装上智能化,享受科技带来的生活便利:还有部分人处于对智能家居的了解期.不管你属于 ...

  7. IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ消息队列

    1.引言 消息是互联网信息的一种表现形式,是人利用计算机进行信息传递的有效载体,比如即时通讯网坛友最熟悉的即时通讯消息就是其具体的表现形式之一. 消息从发送者到接收者的典型传递方式有两种: 1)一种我 ...

  8. 软件测试作业1:正确理解原型方法对软件生命周期不同阶段的支持

    作业1 1.正确理解原型方法对软件生命周期不同阶段的支持,分别给出:辅助或代替分析阶段:辅助设计阶段:代替分析与设计阶段:代替分析.设计和实现阶段:代替全部开发阶段所对应的开发活动执行时间顺序. 答: ...

  9. python函数可以作为容器对象吗_正确理解Python函数是第一类对象

    正确理解 Python函数,能够帮助我们更好地理解 Python 装饰器.匿名函数(lambda).函数式编程等高阶技术. 函数(Function)作为程序语言中不可或缺的一部分,太稀松平常了.但函数 ...

最新文章

  1. 独家 | 为什么要尝试A/B测试的贝叶斯方法(附链接)
  2. 中国团队新型类脑芯片登上《自然》封面
  3. php如何做浏览量,php+ajax实现的点击浏览量加1
  4. C++ int转string的几种方法比较
  5. MVC5网站部署到IIS7
  6. pyplot交互地画多个plot
  7. php周边,PHP周边 – 第6页 – Joyous—快乐由此开始
  8. 如何利用Matlab完成数字1-9的语音识别
  9. 浏览器和node的eventLoop的区别
  10. JDBC数据源(DataSource)的简单实现
  11. 蝶形算法 matlab,FFT快速傅里叶变换(蝶形算法)详解
  12. 字节跳动 —— 2023暑期实习面试
  13. iOS app发布ERROR ITMS-90096
  14. 2048 game (转载)
  15. 激光导弹Gundam Unicorn(二维前缀和and二维差分)
  16. 微信小程序之个人中心静态页面
  17. 网络空间安全是否有必要考研
  18. 初级考试可以使用计算机吗,计算机初级考试的内容都有哪些
  19. LintCode 题解 |亚马逊、微软热门题:目的地的最短路径
  20. PCB板设计之Altium Designer了解以及电子设计基础知识

热门文章

  1. ssm 基于微信小程序美容理发店预约系统app
  2. WebSocket的学习
  3. 浅谈搜狐云景PAAS平台
  4. ML之NB:基于news新闻文本数据集利用纯统计法、kNN、朴素贝叶斯(高斯/多元伯努利/多项式)、线性判别分析LDA、感知器等算法实现文本分类预测
  5. 2021HDU多校第四场5-Didn‘t I Say to Make My Abilities Average in the Next Life?!
  6. Error response from daemon: Container 17ae3dc98507daca0267a8673295ede4cf2d5d5... is not running
  7. 谷歌采用神经网络驱动机器翻译,可离线翻译59种语言
  8. Ubuntu16.04安装Matlab2016b
  9. 微信授权文件放到域名根目录
  10. 微博只显示来自android,新浪微博手机版五大常见问题解决方法