本文同时发表在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》

MySQL 如何执行关联查询的更多相关文章

  1. MySQL如何执行关联查询

    MySQL中‘关联(join)’ 一词包含的意义比一般意义上理解的要更广泛.总的来说,MySQL认为任何一个查询都是一次‘关联’ --并不仅仅是一个查询需要到两个表的匹配才叫关联,索引在MySQL中, ...

  2. mysql如何执行关联查询与优化

    mysql如何执行关联查询与优化 一.前言 在数据库中执行查询(select)在我们工作中是非常常见的,工作中离不开CRUD,在执行查询(select)时,多表关联也非常常见,我们用的也比较多,那么m ...

  3. Mysql多表表关联查询 inner Join left join right join

    Mysql多表表关联查询 inner Join left join right join

  4. JDBC MySQL 多表关联查询查询

    public static void main(String[] args) throws Exception{ Class.forName("com.mysql.jdbc.Driver&q ...

  5. MySQL多表关联查询与存储过程

    --  **************关联查询(多表查询)**************** -- 需求:查询员工及其所在部门(显示员工姓名,部门名称) -- 1.1 交叉连接查询(不推荐.产生笛卡尔乘积 ...

  6. mysql 无法执行select查询

    场景:mysql无法执行select命令查询,对于已存在的数据库,除了mysql.information_schema数据库,其它诸如nova.keystone.cinder等数据库都有此现象. 日志 ...

  7. MySQL 三种关联查询的方式: ON vs USING vs 传统风格

    看看下面三个关联查询的 SQL 语句有何区别? 1SELECT * FROM film JOIN film_actor ON (film.film_id = film_actor.film_id) 2 ...

  8. MySQL多表关联查询数量

    //多表关联查询数量select user, t1.count1, t2.count2from user tleft join ( select user_id, count(sport_type) ...

  9. [MySQL]多表关联查询技巧

    示例表A: author_id author_name 1 Kimmy 2 Abel 3 Bill 4 Berton 示例表B: book_id author_id start_date end_da ...

随机推荐

  1. Shodan在渗透测试及漏洞挖掘中的一些用法

    渗透测试中,第一阶段就是信息搜集,这一阶段完成的如何决定了你之后的进行是否顺利,是否更容易.而关于信息收集的文章网上也是有太多.今天我们来通过一些例子来讲解如何正确使用Shodan这一利器. 想要利用 ...

  2. 如何使用MFC连接Access数据库

    (1)新建一个Access数据库文件.将其命名为data.mdb,并创建好表.字段. (2)为系统添加数据源.打开“控制面板”—>“管理工具”—>“数据源”,选择“系统DSN”,点击右边的 ...

  3. c语言的typedef

    一.typedef作用简介 1.作用:给已经存在的类型起一个新的名称 2.使用场合: 1> 基本数据类型 2> 指针 3> 结构体 4> 枚举 5> 指向函数的指针 * ...

  4. Centos 安装boost库

    1.在http://www.boost.org/下载boost安装包boost_1_65_1.tar.gz 2.在Centos上解压tar -zxvf  boost_1_65_1.tar.gz后,cd ...

  5. HDU4046--Panda(树状数组)

    Panda Time Limit: 10000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Sub ...

  6. phython编写图形界面

    Python支持多种图形界面的第三方库,包括: Tk wxWidgets Qt GTK 等等. 但是Python自带的库是支持Tk的Tkinter,使用Tkinter,无需安装任何包,就可以直接使用. ...

  7. Springboot读取配置文件及自定义配置文件

    1.创建maven工程,在pom文件中添加依赖 <parent> <groupId>org.springframework.boot</groupId> <a ...

  8. SQL Server学习之路(四):“增删改查”之“删”

    0.目录 1.前言 2.通过SSMS删除 3.通过SQL语句删除 3.1 删除单行数据 3.2 删除所有行数据 3.3 删除表和数据库 1.前言 增删改查都是对数据的操作,其中"删" ...

  9. MySQL安装(yum、二进制、源码)

    MySQL安装(yum.二进制.源码) 目录 1.1 yum安装... 2 1.2 二进制安装-mysql-5.7.17. 3 1.2.1 准备工作... 3 1.2.2 解压.移动.授权... 3 ...

  10. springmvc关于前台日期作为实体类对象参数类型转换错误

    页面报错: 后台错误: Field error in object 'user' on field 'birthday': rejected value [2013-06-24]; codes [ty ...