本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/51

当前mysql执行的策略很简单:mysql对任何关联都执行嵌套循环操作,即mysql先在一个表中循环取出单条数据,然后再嵌套循环到下一个表中寻打匹配的行,依次下去,直到描述到所表表中匹配的行为止。然后根据各个表匹配的行,返回查询中需要的各个列。mysql会尝试在最后一个关联表中打到所有匹配的行,如果最后一个关联表无法找到更多的行以后,mysql返回到上一层次关联表,看是否能够找到更多的匹配记录,依此类推迭代执行。

按照这样的方式查找第一条表记录,再嵌套查询下一个关联表,然后回溯到上一个表,在mysql中是通过嵌套循环的方式来实现的--正如其名‘嵌套循环关联’。请看下面的例子中的简单查询:

select tbl1.col1,tbl2.col2
from tbl1 inner join tbl2 using(col3)
where tbl1.col1 in(5,6);

假设mysql按照查询中的表顺序进行关联操作,我们则可以用下面的伪代码表示mysql将如何完成这个查询:

outer_iter = iterator_over tbl1 where col1 in(3,4)
outer_row = outer_iter.next
while outer_row
inner_iter = iterator over tbl2 where col3=outer_row.col3
inner_row = inner_iter.next
while inner_row
  output[outer_row.col1,inner_row.col2]
inner_row = inner_iter.next
end
out_row = outer_iter.next
end

上面的执行计划对于单表查询和多表关联查询都适用,如果是一个单表查询,那么只需要完成上面的外层的基本操作。对于外连接和上面的执行过程任然适用。例如我们将上面的查询修改如下:

SELECT tbl1.col1 ,tbl2.col2 FROM tbl1 left outer join tbl2 using (col3) WHERE tbl1.col1 in (3,4)

对应的伪代码:

outer_iter = iterator over tbl1 where col1 in(3,4)
outer row = outer_iter.next
while outer_row
inner_iter = iterator over tbl2 where col3 = outer_row.col3
inner_row = inner_iter.next
if inner row -> 手动加粗
while inner_row
out_put [outer_row.col1,inner_row.col2]
inner_row = inner_iter.next
end
else -> 手动加粗
out_put[outer_row.col1,NULL] -> 手动加粗
end
outer_row = outer_iter.next
end

从上面两个例子也可以看出,对于主表来说,是先进行主表的where条件筛选,再进行表联接,而不是先进行整表联接再进行where条件的筛选。

举个例子:

数据表结构:

mysql> create table a(
-> id int unsigned not null primary key
-> ); mysql> create table b like a;

表中数据:

mysql> select * from a;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
+----+
5 rows in set (0.00 sec) mysql> select * from b;
+----+
| id |
+----+
| 4 |
| 5 |
| 6 |
| 7 |
+----+
4 rows in set (0.00 sec)

explain查询:

mysql> explain select a.id as aid,b.id as bid from a left join b using(id) where a.id>3;
+----+-------------+-------+--------+---------------+---------+---------+----------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+----------+------+--------------------------+
| 1 | SIMPLE | a | range | PRIMARY | PRIMARY | 4 | NULL | 3 | Using where; Using index |
| 1 | SIMPLE | b | eq_ref | PRIMARY | PRIMARY | 4 | com.a.id | 1 | Using index |
+----+-------------+-------+--------+---------------+---------+---------+----------+------+--------------------------+
2 rows in set (0.00 sec)

可以看出,首先在a表上进行范围查询,筛选出a.id>3的数据,然后在进行"嵌套查询"。

注意,on后面的筛选条件主要是针对的是关联表,而对于主表筛选并不适用,比如:

mysql>  select a.id as aid,b.id as bid from a left join b on a.id=b.id and a.id>3;
+-----+------+
| aid | bid |
+-----+------+
| 1 | NULL |
| 2 | NULL |
| 3 | NULL |
| 4 | 4 |
| 5 | 5 |
+-----+------+
5 rows in set (0.00 sec) mysql> explain select a.id as aid,b.id as bid from a left join b on a.id=b.id and a.id>3;
+----+-------------+-------+--------+---------------+---------+---------+----------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+----------+------+-------------+
| 1 | SIMPLE | a | index | NULL | PRIMARY | 4 | NULL | 5 | Using index |
| 1 | SIMPLE | b | eq_ref | PRIMARY | PRIMARY | 4 | com.a.id | 1 | Using index |
+----+-------------+-------+--------+---------------+---------+---------+----------+------+-------------+
2 rows in set (0.00 sec)

我们发现a表的id<=3的数据并未被筛选走,explain的结果是a表进行了index类型的查询,即主键索引的全部扫描。

如果在on的筛选条件是针对b表的呢,情况会怎么样?

下面的例子数据表结构和数据变了,我们只关注查询的结果区别:

mysql> select * from a left join b on a.data=b.data and b.id<=20;
+----+------+------+------+
| id | data | id | data |
+----+------+------+------+
| 1 | 1 | NULL | NULL |
| 2 | 2 | NULL | NULL |
| 3 | 3 | NULL | NULL |
| 4 | 4 | 1 | 4 |
| 4 | 4 | 6 | 4 |
| 4 | 4 | 11 | 4 |
| 4 | 4 | 16 | 4 |
| 5 | 5 | 2 | 5 |
| 5 | 5 | 7 | 5 |
| 5 | 5 | 12 | 5 |
| 5 | 5 | 17 | 5 |
+----+------+------+------+
11 rows in set (0.00 sec) mysql> select * from a left join b on a.data=b.data where b.id<=20;
+----+------+------+------+
| id | data | id | data |
+----+------+------+------+
| 4 | 4 | 1 | 4 |
| 5 | 5 | 2 | 5 |
| 4 | 4 | 6 | 4 |
| 5 | 5 | 7 | 5 |
| 4 | 4 | 11 | 4 |
| 5 | 5 | 12 | 5 |
| 4 | 4 | 16 | 4 |
| 5 | 5 | 17 | 5 |
+----+------+------+------+
8 rows in set (0.00 sec)

由此,我们可以根据伪码来分析两者的区别:

outer_iter = iterator over a
outer row = outer_iter.next
while outer_row
inner_iter = iterator over b where data = outer_row.date where id<=20
inner_row = inner_iter.next
if inner row
while inner_row
out_put [outer_row,inner_row]
inner_row = inner_iter.next
end
else
out_put[outer_row,NULL]
end
outer_row = outer_iter.next
end
outer_iter = iterator over a
outer row = outer_iter.next
while outer_row
inner_iter = iterator over b where data = outer_row.date ->手动加粗
inner_row = inner_iter.next
if inner row
while inner_row
out_put [outer_row,inner_row]
inner_row = inner_iter.next
end
else
out_put[outer_row,NULL]
end
outer_row = outer_iter.next
end
left join的结果集中 where b.id<=20 ->手动加粗

参考资料:《高性能MySQL》

最新文章

  1. hadoopfs: 未找到命令...
  2. PHP使用JSON通信
  3. 【转】持久化消息队列之MEMCACHEQ
  4. Unsupported configuration attributes: [FILE_UPLOAD]
  5. virtualenv中ImportError: No module named django
  6. cvReleaseImage 释放内存出错
  7. YTU 3005: 皇后问题(栈和队列)
  8. Python文件格式化写入
  9. CSS 知识积累
  10. 2016 系统设计第一期 (档案一)MVC form数据提交
  11. 【ArcGIS 10.2新特性】ArcGIS Online新特性(上)
  12. C# 实验4 数据库
  13. python搭建opencv
  14. alloc_page分配内存空间--Linux内存管理(十七)
  15. NodeJS笔记(五) 使用React Native 创建第一个 Android APP
  16. OkHttp 入门篇
  17. 2emq服务器压力测试(无用)
  18. 理解Python的双下划线命名(转)
  19. MySQL group replication介绍
  20. centos6.8下源码编译安装tmux

热门文章

  1. 十、Hadoop学习笔记————Hive与Hbase以及RDBMS(关系型数据库)的关系
  2. Heap Sorting 总结 (C++)
  3. 全内存的redis用习惯了?使用基于硬盘存储类似redis的nosql产品ssdb呢?
  4. 学习爬虫的day03 (通过代理去爬去数据)
  5. ASP.NET Core中的OWASP Top 10 十大风险-跨站点脚本攻击 (XSS)
  6. CSS3 box-shadow 内外阴影效果
  7. SQL-删除重复记录
  8. 【c语言】实现一个函数,求字符串的长度,不同意创建第三方变量
  9. cocos2dx-3.0(14)------SpriteBatchNode与SpriteFrameCache加快渲染
  10. HDU-1828-Picture(线段树)