mysql连接包有区别嘛_MySQL连接查询区别及原理

在实操之中,对于join、left join、right join通常情况下也是仅仅用到了left join,对于其他几种,心存疑惑,因此对几种join查询、以及原理做个记录。

11f3c8b6d0e6e722b7392d7efcf47e35.png

1.left、right、inner join 的区别

创建表t1、t2

CREATE TABLE`t2` (

`id`int(11) NOT NULL,

`a`int(11) DEFAULT NULL,

`b`int(11) DEFAULT NULL,PRIMARY KEY(`id`),KEY`a` (`a`)

) ENGINE=InnoDB;

​create table t1 like t2;

t1、t2分别插入5条数据

delimiter ;;create procedureidata()begin

declare i int;set i=1;while(i<=5)doinsert into t1 values(i, i, i);set i=i+1;end while;end;;

delimiter ;

call idata();

​insert into t2 select * from t1 where a <= 4;insert into t2 values(6, 6, 6);

left join 会查询出左表所有的数据,以及右表能连接上的字段

select * from t1 left join t2 on t1.id = t2.id;

fbe8e14a24ff282181b4e24662a767b8.png

right join 会查询出右表所有的数据,以及左表能连接上的字段

select * from t1 right join t2 on t1.id = t2.id;

eb64139d2c96543ed38f141f2fbc39ce.png

inner join(等值连接) 只返回两个表中联结字段相等的行

select * from t1 inner join t2 on t1.id = t2.id;

4bc2fd72d399faae553f1880ced06cf0.png

2.连接查询原理

为了后面结果更为清晰,往t1再插入15条数据:

drop procedureidata;

delimiter ;;create procedureidata()begin

declare i int;set i=6;while(i<=20)doinsert into t1 values(i, i, i);set i=i+1;end while;end;;

delimiter ;

call idata();

如果直接使用join语句,MySQL优化器可能会选择表t1或t2作为驱动表,会影响分析SQL语句的执行过程。所以使用straight_join让MySQL使用固定的连接方式执行查询,这样优化器只会按照指定的方式去join。

走索引的情况:

通过a字段连接查询:

explain select * from t2 straight_join t1 on t1.a = t2.a;

t2作为驱动表,t1作为被驱动表,这条语句的explain结果:

2499bb4ee801ac603774c818b62713be.png

因为被驱动表t1的字段a有索引,join过程用上了这个索引,因此这个语句的执行流程是这样的:

从表t2中读入一行数据 R;

从数据行R中,取出a字段到表t1里去查找;

取出表t1中满足条件的行,跟R组成一行,作为结果集的一部分;

重复执行步骤1到3,直到表t2的末尾循环结束。

这个过程称之为Index Nested-Loop Join

在这个流程中,扫描t2表5行、之后根据t2.a去表t1中查找,走的是树搜索过程、因此每次扫描一行,总扫描行数为10行。

不走索引的情况

我们再用b字段关联查询

explain select * from t2 straight_join t1 on t1.b = t2.b;

explain结果如下:

c7f0b261954e71270a7f7a3f2b6fd797.png

由于表t1的字段b上没有索引,所以每次都要把t1表中的每一行,拿出来进行对比, mysql采用的是Block Nested-Loop Join,Extra可以看到。该算法并没有将表t1全表扫描20次,它的执行流程为:

把表t2的数据读入线程内存join_buffer中,由于这个语句中写的是select *,因此是把整个表t2放入了内存;

扫描表t1,把表t1中的每一行取出来,跟join_buffer中的数据做对比,满足join条件的,作为结果集的一部分返回。

在这个过程中,对表t1和t2都做了一次全表扫描,因此总扫描次数为25,由于join_buffer是以无序数组的方式组织的,因此对表t1中的每一行,都要做20次判断,总共需要在内存中做的判断次数是:20*5=100次。

因为使用到了join_buffer,而join_buffer大小是有限的,由join_buffer_size设定,默认为256k。如果一次放不下t2中的所有字段,就会采用分段放的策略,执行策略如下:

假如t2表足够大,扫描表t2,顺序读取数据行放入join_buffer中,放完第n行join_buffer满了,继续第2步;

扫描表t1,把t1中的每一行取出来,跟join_buffer中的数据做对比,满足join条件的,作为结果集的一部分返回;

清空join_buffer;

继续扫描表t2,顺序读取最后的m行数据放入join_buffer中,继续执行第2步。

因此表t1可能会被扫描多次,在决定哪个表做驱动表的时候,应该是两个表按照各自的条件过滤,过滤完成之后,计算参与join的各个字段的总数据量,数据量小的那个表,就是“小表”,应该作为驱动表。

三个结论:

如果可以使用被驱动表的索引,join语句还是有其优势的;

不能使用被驱动表的索引,只能使用Block Nested-Loop Join算法,这样的语句就尽量不要使用;

在使用join的时候,应该让小表做驱动表。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值