MySQL中“关联”一次所包含的意义比一般意义上理解的要更广泛。总的来说,MySQL认为任何一个查询都是一次“关联”——并不仅仅是一个查询需要到两个表匹配才叫关联,所以在MySQL中,每一个查询,每一个片段(包括子查询,甚至基于单表的SELECT)都可能是关联。
所以理解MySQL如何执行关联查询至关重要。我们先来看一个UNION查询的例子。对于UNION查询,MySQL先将一系列的单个查询结果放到一个临时表中,然后再重新读出临时表数据来完成UNION查询。在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将如何完成这个查询:
List tbl1 = tbl中col1为5,6的行;
for(tbrow : tb1){
List tb2 = (tbl2中col3=tbrow中col3的行)
for(tb2row : tb2){
输出【tbl1.col1,tbl2.col2】
}
}
上面的查询对于单表和多变关联查询都适用,如果是一个单表查询,那么只需完成上面外层的基本操作。对于外连接上面的执行过程仍然适用。例如,我们将上面查询修改如下:
mysql>select tbl1.col1,tbl2.col2
->from tbl1 left join tbl2 using(col3)
->where tbl1.col1 in(5,6)
对应的伪代码如下,我们用黑体标示出不同的部分:
List tbl1 = tbl中col1为5,6的行;
for(tb1row : tb1){
List tb2 = (tbl2中col3=tb1row中col3的行)
for(tb2row : tb2){
if(tbl1row.col3== tbl12row.col3) {
输出[tbl1.col1, tbl2.col2]
}else{
输出【tbl1.col1,null】
}
}
}
从本质上说,MySQL对所有的类型的查询都以同样的方式运行。例如,MySQL在FROM子句中遇到子查询时,先执行子查询并将其结果放到一个临时表中,然后将这个临时表当作一个普通表对待(正如其名“派生表”)。MySQL在执行UNION查询时也使用类似的临时表,在遇到又外连接的时候MySQL将其改为等价的左外连接。简而言之MysQL会将所有的查询类型都转换成类似的执行计划。
执行计划
上面的查询对于单表和多变关联查询都适用,如果是一个单表查询,那么只需完成上面外层的基本操作。对于外连接上面的执行过程仍然适用。