mysql之left join、join的on、where区别看这篇就懂

前言:

  对于外连接查询,我们都知道驱动表和被驱动表的关联关系条件我们放在 on后面,如果额外增加对驱动表过滤条件、被驱动表过滤条件,放 on 或者 where 好像都不会报错,但是得到的结果集确是不一样的。

  网上大量关于left join、join的on、where区别其实很多都是错误,本文开始揭晓其中区别所在,该如何使用。

1.准备工作

  建表语句

CREATE TABLE `t_students` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `class_id` int(11) NOT NULL,
  `name` varchar(10) NOT NULL,
  `gender` char(1) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_class_id` (`class_id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8

CREATE TABLE `t_classes` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8

  表数据

在这里插入图片描述

在这里插入图片描述

2. Join 连接on、where区别

  sql如下:

SELECT * FROM `t_students` ts JOIN `t_classes` tc ON ts.`class_id` = tc.`id`;

  执行 explain extended + sql; 命令

select_typetabletypepossible_keyskeykey_lenrefrowsExtra
SIMPLEtcALL(NULL)(NULL)(NULL)(NULL)11
SIMPLEtsALLPRIMARY(NULL)(NULL)(NULL)5Using where; Using join buffer

  执行 show warnings; (该命令可以查看优化器优化后真正执行的sql语句)

select `mytest`.`ts`.`id` AS `id`,`mytest`.`ts`.`class_id` AS `class_id`,`mytest`.`ts`.`name` AS `name`,`mytest`.`ts`.`gender` AS `gender`,`mytest`.`tc`.`id` AS `id`,`mytest`.`tc`.`name` AS `name` 

from `mytest`.`t_students` `ts` join `mytest`.`t_classes` `tc` 

where (`mytest`.`tc`.`id` = `mytest`.`ts`.`class_id`)

  分析show warnings展示了优化后的语句,可以发现 on 连接条件被转化为 where 过滤条件。更多案例,可以自己去测试,on 都会转化为 where

  结论:对于Join连接,on和where其实是一样的,经过InnoDB优化后,on连接条件会转化为where。

3. left join之on、where区别

3.1 驱动表之on、where区别

  sql如下:

SELECT * FROM `t_students` ts 
LEFT JOIN `t_classes` tc ON ts.`class_id` = tc.`id` AND ts.`gender` = 'M';

  执行 explain extended + sql;

select_typetabletypepossible_keyskeykey_lenrefrowsExtra
SIMPLEtsALL(NULL)(NULL)(NULL)(NULL)11
SIMPLEtceq_refPRIMARYPRIMARY4mytest.ts.class_id1

  执行 show warnings;

select `mytest`.`ts`.`id` AS `id`,`mytest`.`ts`.`class_id` AS `class_id`,`mytest`.`ts`.`name` AS `name`,`mytest`.`ts`.`gender` AS `gender`,`mytest`.`tc`.`id` AS `id`,`mytest`.`tc`.`name` AS `name` 

from `mytest`.`t_students` `ts` left join `mytest`.`t_classes` `tc` 

on(((`mytest`.`ts`.`class_id` = `mytest`.`tc`.`id`) and (`mytest`.`ts`.`gender` = 'M'))) 

where 1

  结果集:

在这里插入图片描述

  分析:从结果集来看,ts.gender = ‘M’ 并未生效。为什么?

  从 explian 分析看出,ts 作为驱动表,做全表扫描,然后把查询到的每条记录的 ts.class_idts.gender= 'M' (也就是 on 条件里面的)作为条件让被驱动表tc 做单表查询(ts有多少条记录,单表查询多少次)得到结果集。

  结论:left join 连接 on连接条件是给被驱动表用的,ts.gender = 'M' 放在 on连接条件里面,对驱动表查询是无效的,仅在连接被驱动表时生效,这不是我们想要的结果。

  那我们应该怎么改sql,让 ts.gender = 'M' 对驱动表生效呢?

  修改后的 sql 如下:

SELECT * FROM `t_students` ts LEFT JOIN `t_classes` tc
ON ts.`class_id` = tc.`id` WHERE ts.`gender` = 'M';

  执行explain extended + sql;

select_typetabletypepossible_keyskeykey_lenrefrowsExtra
SIMPLEtsALL(NULL)(NULL)(NULL)(NULL)11Using where
SIMPLEtceq_refPRIMARYPRIMARY4mytest.ts.class_id1

  可以看出,ts表的 Extra 使用了 Using where

  执行 show warnings;

select `mytest`.`ts`.`id` AS `id`,`mytest`.`ts`.`class_id` AS `class_id`,`mytest`.`ts`.`name` AS `name`,`mytest`.`ts`.`gender` AS `gender`,`mytest`.`tc`.`id` AS `id`,`mytest`.`tc`.`name` AS `name` 

from `mytest`.`t_students` `ts` left join `mytest`.`t_classes` `tc`

on((`mytest`.`tc`.`id` = `mytest`.`ts`.`class_id`)) 

where (`mytest`.`ts`.`gender` = 'M')

  结果集:

在这里插入图片描述

  分析

  从 explian 分析看出,ts作为驱动表,把 ts.gender = 'M' 作为条件做全表扫描,然后把查询到的每条记录的 ts.class_id(也就是 on连接条件)作为条件让被驱动表tc 做单表查询(ts有多少条记录,单表查询多少次)得到结果集。

  可以看出, ts.gender = 'M' 放在where条件里面,驱动表做全表扫描时会带上where条件。

  结论:对于驱动表,需要加针对驱动表的过滤条件,我们应该放在 where条件而不是 on条件

3.2 被驱动表之on、where区别

  sql如下:

SELECT * FROM `t_students` ts LEFT JOIN `t_classes` tc
ON ts.`class_id` = tc.`id` AND tc.`name` IN ( '二班', '三班');

  执行 explain extended + sql;

select_typetabletypepossible_keyskeykey_lenrefrowsExtra
SIMPLEtsALL(NULL)(NULL)(NULL)(NULL)11
SIMPLEtceq_refPRIMARYPRIMARY4mytest.ts.class_id1

  执行 show warnings;

select `mytest`.`ts`.`id` AS `id`,`mytest`.`ts`.`class_id` AS `class_id`,`mytest`.`ts`.`name` AS `name`,`mytest`.`ts`.`gender` AS `gender`,`mytest`.`tc`.`id` AS `id`,`mytest`.`tc`.`name` AS `name` 

from `mytest`.`t_students` `ts` left join `mytest`.`t_classes` `tc` 

on(((`mytest`.`ts`.`class_id` = `mytest`.`tc`.`id`) and (`mytest`.`tc`.`name` in ('二班','三班')))) 

where 1

  结果集:

在这里插入图片描述

  分析

  从 explian 分析看出,ts 作为驱动表,做全表扫描,然后把查询到的每条记录的 ts.class_idtc.name in ('二班','三班') (也就是 on条件)作为条件让被驱动表tc 做单表查询(ts有多少条记录,单表查询多少次)得到结果集。

  假如:被驱动表的过滤条件放在 where 而不是 on呢,请看如下sql:

SELECT * FROM `t_students` ts LEFT JOIN `t_classes` tc
ON ts.`class_id` = tc.`id` WHERE tc.`name` IN ( '二班', '三班');

  执行 explain extended + sql;

select_typetabletypepossible_keyskeykey_lenrefrowsExtra
SIMPLEtsALL(NULL)(NULL)(NULL)(NULL)11
SIMPLEtcALLPRIMARY(NULL)(NULL)(NULL)5Using where; Using join buffer

  执行 show warnings;

select `mytest`.`ts`.`id` AS `id`,`mytest`.`ts`.`class_id` AS `class_id`,`mytest`.`ts`.`name` AS `name`,`mytest`.`ts`.`gender` AS `gender`,`mytest`.`tc`.`id` AS `id`,`mytest`.`tc`.`name` AS `name` 

from `mytest`.`t_students` `ts` join `mytest`.`t_classes` `tc` 

where ((`mytest`.`tc`.`id` = `mytest`.`ts`.`class_id`) and (`mytest`.`tc`.`name` in ('二班','三班')))

  仔细看这里,left join连接变成了 join连接

  结果集:

在这里插入图片描述

  分析

  从 show warnings 分析看出,如果被驱动表有过滤条件在 where,那么 left join 会失效,会被优化成 join 连接。所以,被驱动表的过滤条件应该放在 on而不是 where

4. 附加

  网上有种说法:left join连接 on会先生成虚拟表,然后再经过where条件过滤生成结果集。

  这种说法是错误的!

  验证:

  sql如下:

SELECT * FROM `t_classes` tc LEFT JOIN `t_students` ts 
ON ts.`class_id` = tc.`id` WHERE ts.id = NULL
  • 虚拟表生成:
SELECT * FROM `t_classes` tc LEFT JOIN `t_students` ts 
ON ts.`class_id` = tc.`id`

  结果集如下:

在这里插入图片描述

  • 再经过
WHERE ts.id = NULL

  生成结果集,应该如下:

在这里插入图片描述

  然后我们执行这条sql 生成的结果集却是如下所示:

在这里插入图片描述

  原因:left join 被优化成了 join。

  • 18
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
MySQL中,LEFT JOIN是一种连接查询的方式,可以链接两个或多个表格。当使用LEFT JOIN时,将左边的表格的所有记录与右边表格的匹配记录连接起来,而不管右边表格的记录是否存在。 在LEFT JOIN中,将条件放在ON和WHERE子句中的区别如下: 1. ON子句:这是在连接两个表格时使用的条件。在ON子句中,我们可以指定要使用的列进行连接,例如:ON table1.column = table2.column。通过在ON子句中指定连接条件,可以确保将两个表格中匹配的记录连接起来。如果在ON子句中省略连接条件,则会返回左边表格的所有记录与右边表格的每条记录的组合。 2. WHERE子句:该子句用于在连接两个表格之后对结果进行筛选。在WHERE子句中,我们可以使用条件对连接后的结果进行筛选,例如:WHERE column1 = value。通过在WHERE子句中指定过滤条件,可以从连接后的结果中选择满足条件的记录。如果在WHERE子句中省略条件,则返回连接后的所有记录。 因此,ON子句用于连接两个表格,而WHERE子句用于筛选连接后的结果。放置条件的不同位置会对查询结果产生不同的影响。当连接条件放在ON子句中时,会在连接时对两个表格进行匹配。而当条件放在WHERE子句中时,会在连接后对匹配的结果进行过滤。 总结:ON子句用于连接表格,WHERE子句用于筛选连接后的结果。根据具体需求,我们可以选择将条件放置在ON子句或WHERE子句中来实现不同的查询目的。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值