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

mysql之left join、join的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区别

sql1

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

使用explain extended + sql;

select_type table type possible_keys key key_len ref rows Extra
SIMPLE tc ALL (NULL) (NULL) (NULL) (NULL) 11
SIMPLE ts ALL PRIMARY (NULL) (NULL) (NULL) 5 Using 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`)

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

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

3. left join之on、where区别
  • 驱动表之on、where区别

sql1

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

使用explain extended + sql;

select_type table type possible_keys key key_len ref rows Extra
SIMPLE ts ALL (NULL) (NULL) (NULL) (NULL) 11
SIMPLE tc eq_ref PRIMARY PRIMARY 4 mytest.ts.class_id 1

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_id、ts.gender = ‘M’(也就是on里面的)作为条件让被驱动表tc做单表查询(ts有多少条记录,单表查询多少次)得到结果集。

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

那我们应该怎么改sql,让ts.gender = 'M’对驱动表生效。请看sql2

sql2

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

使用explain extended + sql;

select_type table type possible_keys key key_len ref rows Extra
SIMPLE ts ALL (NULL) (NULL) (NULL) (NULL) 11 Using where
SIMPLE tc eq_ref PRIMARY PRIMARY 4 mytest.ts.class_id 1

可以看出,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里面

  • 被驱动表之on、where区别

sql1

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

使用explain extended + sql;

select_type table type possible_keys key key_len ref rows Extra
SIMPLE ts ALL (NULL) (NULL) (NULL) (NULL) 11
SIMPLE tc eq_ref PRIMARY PRIMARY 4 mytest.ts.class_id 1

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_id、tc.name in (‘二班’,‘三班’)(也就是on里面的)作为条件让被驱动表tc做单表查询(ts有多少条记录,单表查询多少次)得到结果集。

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

sql2

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

使用explain extended + sql;

select_type table type possible_keys key key_len ref rows Extra
SIMPLE ts ALL (NULL) (NULL) (NULL) (NULL) 11
SIMPLE tc ALL PRIMARY (NULL) (NULL) (NULL) 5 Using 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 ('二班','三班')))

结果集:

在这里插入图片描述

分析

从show warnings分析看出,如果被驱动表有过滤条件在where,那么left join会失效,InnoDB优化成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 生成的结果集却是如下所示:

在这里插入图片描述

原因:被驱动表的过滤条件放在where的话,left join被优化成了join。

猜你喜欢

转载自blog.csdn.net/weixin_44981707/article/details/110739121