MySQL如何执行关联查询
MySQL中关联一次意义比较广泛,并不仅仅是一个查询需要到2个表匹配才叫关联,而是任何一个查询都是一次关联,每一个查询,片段(包括子查询单表的查询)都可能是关联
当前 MySQL 关联执行策略很简单: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
上面的执行计划对于单表查询和多表关联查询都适用,如果是一个单表查询,那么只需要完成上面的外层的基本操作。对于外连接和上面的执行过程任然适用。例如我们将上面的查询修改如下:
mysql> 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
另一种可视化查询执行计划的方法是根据优化器执行的路径绘制出 对应的“泳道图”请从左至右,从上至下地看这幅图。
从本质上说,MySQL对所有的类型的查询都以同样的方式运行。 例如,MySQL在FROM子句中遇到子查询时,先执行子查询并将其结果 放到一个临时表中,然后将这个临时表当作一个普通表对待(正如 其名“派生表”)。MySQL在执行UNION查询时也使用类似的临时表,在遇到右外连接的时候,MySQL将其改写成等价的左外连接。简而言之,当前版本的MySQL会将所有的查询类型都转换成类似的执行计划
不过,不是所有的查询都可以转换成上面的形式。例如,全外连接 就无法通过嵌套循环和回溯的方式完成,这时当发现关联表中没有找到 任何匹配行的时候,则可能是因为关联是恰好从一个没有任何匹配的表 开始。这大概也是MySQL并不支持全外连接的原因。还有些场景,虽 然可以转换成嵌套循环的方式,但是效率却非常差
MySQL 生成查询的一颗指令树,然后通过存储引擎执行完成这颗指令树big返回结果,任何多表查询都可以使用一棵树表示
在计算机科学中,这被称为一颗平衡树。但是,这并不是MySQL 执行查询的方式。正如我们前面章节介绍的,MySQL总是从一个表开 始一直嵌套循环、回溯完成所有表关联。所以,MySQL的执行计划总是一棵左测深度优先的树
mysql> explain select u.id,c.id from sys_user u left join sys_area c on c.id = u.city_id where u.id > 3;
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+------+----------+-------------+
| 1 | SIMPLE | u | NULL | range | PRIMARY | PRIMARY | 8 | NULL | 10 | 100.00 | Using where |
| 1 | SIMPLE | c | NULL | eq_ref | PRIMARY | PRIMARY | 8 | hr.u.city_id | 1 | 100.00 | Using index |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+------+----------+-------------+
2 rows in set (0.06 sec)
可以看出,首先在 sys_user 表上进行范围查询,筛选出 u.id > 3 的数据,然后在进行"嵌套查询"。