联表查询做开发的小伙伴会经常使用,但是可以大家都比较少去深入了解MySQL是怎么执行多表联表查询的,比如怎么选择驱动表(第一个被处理的表),是先联表还是说先根据where条件(前提是有where条件)进行过滤后在进行联表搜索符合条件的结果,等等。那这个里面可能涉及到的知识点比较多,就不仅仅是今天要讨论的驱动表的选择,还需要小伙伴们去了解更多相关的知识,比如:索引 ICP,nested_loop算法等等。后面笔者将更新相关的文章,供小伙伴们参考。
1. 联表的类型
我们经常使用的多表连接类型有以下三类:
1) 左连接(left join)
2) 右连接(right join)
3) 内连接(inner join)
关于这三种连接的区别,笔者在这里就不多说了,不了解的小伙伴们可以查阅相关的资料。
下面针对这三种连接分别做测试。
2.实践
在这里小伙伴们自己要准备两张表,笔者这里准备的是app,app_ext 这两张表,笔者这两张表比较复杂,这里就不全部展示出来了,需要注意的是笔者这里的mysql版本是8.0的。
1) 左连接和右连接
执行 explain select a.id,a.name,b.grade from app a left join app_ext b on a.id = b.appid; 其中a.id 是主键并且是自增的,app_ext 中的appid 与app.id 是关联的。输出结果如下:

我们可以看到驱动表是我们的a表,下面我们改成right join 看下:
explain select a.id,a.name,b.grade from app a right join app_ext b on a.id = b.appid;

可以看到mysql 选择了b表作为驱动表。
其实从上面的运行结果我们可以得出,左连接或者右连接的情况下是不管表中的记录数的,如果是左连接会选择左边的表作为驱动表,如果是右连接会选择右边的表作为驱动表。
2) 内连接
explain select a.id,a.name,b.grade from app a inner join app_ext b on a.id = b.appid;
输出结果如下:

此时mysql 选择了b表作为了驱动表。下面我们再加些条件来看下运行结果:
explain select a.id,a.name,b.grade from app a inner join app_ext b on a.id = b.appid where a.name = '剑仙缘';

我们看到mysql仍然是选择b表作为驱动表,我们再来单独分析下这条sql语句:
mysql > select count(id) from app where name = '剑仙缘';
+-----------+
| count(id) |
+-----------+
| 2 |
+-----------+
可以看到满足条件的记录实际上只有两行,下面使用explain 来看下这条语句的执行计划。
mysql > explain select id,name from app where name = '剑仙缘';

我们发现执行计划里面的rows仍然是需要扫描8137行数据,所以加上这个where条件并不能改变mysql所选择的驱动表。那如果where的条件是有索引的情况了,比如利用user_id字段来检索,下图是笔者app数据表里面的索引:

mysql > explain select a.id,a.name,b.grade from app a inner join app_ext b on a.id = b.appid where a.user_id= 1079;

我们发现此时mysql选择了a表作为了驱动表,我们再来单独分析下select id,name from app where user_id = 1079;这条语句:
mysql > explain select id,name from app where user_id = 1079;

我们发现此时mysql扫描的次数大大减少了,原因很简单,因为mysql利用了索引,所以无需全部扫描。
总结:对于内连接mysql会选择扫描次数比较少的作为驱动表。
最后一点:我们从上面的测试结果中可以看出:当查询语句里面的where条件涉及到索引时,mysql会事先根据索引去过滤到一部分不符合条件的数据,然后再去和另外一张表的数据做匹配,反之,则会对全表进行扫描,等把所有的记录都查询出来再做过滤,这其实也是mysql内部做的一个优化-索引ICP,笔者将在后面的文章里面做详细的介绍。
好了,本篇文章就介绍到这里了,有不对的地方,欢迎小伙伴们指出,看到会及时回复的。
网友评论