总的来说,mysql认为任何一个查询都是一次关联,并不仅仅是一个查询需要用到两个表匹配才叫关联,所以,在mysql中,每一个查询,每一个片段(包括子查询,甚至单表select)都可能是关联。所以,理解mysql如何执行关联查询至关重要,先来看一个union的例子,对于union,mysql先将一系列的单个查询结果放到一个临时表中,然后再重新读出临时表数据来完成union,在mysql的概念中,每个查询都是一次关联,所以读取结果临时表也是一次关联。

当前mysql关联执行的策略很简单,mysql对任何关联都执行嵌套循环关联操作,即,mysql先在一个表中循环读取单条数据,然后再嵌套循环到下一个表中寻找匹配的行,依次下去,直到找到所有表中匹配的行为止。然后根据各个表匹配的行,返回查询中需要的各个列,mysql会尝试在最后一个关联表中找到所有匹配的行,如果最后一个关联表无法找到更多的行以后,mysql返回到上一层次关联表,看是否能够找到更多的匹配记录,依次类推迭代执行。照这样的方法查找第一个表记录,再嵌套查询下一个关联表,然后回溯到上一个表,在mysql中是通过嵌套循环的方式实现(正如其名:嵌套循环关联)

从本质上说,mysql对所有的类型的查询都以同样的方式运行,如:mysql在from子句中遇到子查询,先执行子查询并将结果放到一个临时表中,然后将这个临时表当作一个普通表对待(正如其名:派生表),mysql在执行union查询时也使用类似的临时表,在遇到右外连接是,mysql将其改写成等价的左外连接,换而言之,当前版本的mysql会将所有的查询类型都换换成类似的执行计划,不过,不是所有的查询都可以转换成上面的形式,如:全外连接就无法通过嵌套循环和回溯的方式完成,这也是mysql并不支持全外连接的原因。

注意:mysql临时表没有任何索引,在编写复杂的子查询和关联查询的时候需要注意这一点,这一点对union查询也一样,在mysql5.6和mariadb中有了重大改变,这两个版本都引入了更加复杂的执行计划。

执行计划:

和很多其他关系数据库不同,mysql并不会生成查询字节码来执行查询,mysql生成查询的一棵指令树,然后通过存储引擎执行完成这棵指令树并返回结果,最终的执行计划包含了重构查询的全部信息,如对某个查询执行explain extended后,再执行show warnings,就可以查看到重构的查询。 mysql总是从一个表开始一直嵌套循环,回溯完成所有表的关联。所以,mysql的执行计划是一棵左侧深度优先的树,然后回溯到上一层关联:

关联查询优化器:

mysql优化器最重要的一部分就是关联查询优化,它决定了多个表关联时的顺序,通常多表关联的时候,可以有多种不同的关联顺序来获得相同的执行效果,关联查询优化器则通过评估不同顺序时的成本来选择一个代价最小的关联顺序。

示例:

explain select film.film_id,film.title,film.release_year,actor.actor_id,actor.first_name,actor.last_name from sakila.film join sakila.film_actor using(film_id) join sakila.actor using(actor_id)\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: actor

type: ALL

possible_keys: PRIMARY

key: NULL

key_len: NULL

ref: NULL

rows: 200

Extra: NULL

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: film_actor

type: ref

possible_keys: PRIMARY,idx_fk_film_id

key: PRIMARY

key_len: 2

ref: sakila.actor.actor_id

rows: 13

Extra: Using index

*************************** 3. row ***************************

id: 1

select_type: SIMPLE

table: film

type: eq_ref

possible_keys: PRIMARY

key: PRIMARY

key_len: 2

ref: sakila.film_actor.film_id

rows: 1

Extra: NULL

3 rows in set (0.00 sec)

执行计划中可以看到,mysql从actor表开始,使用film_actor表的索引film_id来查找对应的actor_id值,然后根据film表的主键找到对应的记录,oracle用户会用下面的术语描述:actor表作为驱动表先找到file_actor表,然后以此结果为驱动表再查找film表。

下面使用straight_join关键字指定关联表顺序,如下:

explain select straight_join film.film_id,film.title,film.release_year,actor.actor_id,actor.first_name,actor.last_name from sakila.film join sakila.film_actor using(film_id) join sakila.actor using(actor_id)\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: film

type: ALL

possible_keys: PRIMARY

key: NULL

key_len: NULL

ref: NULL

rows: 1000

Extra: NULL

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: film_actor

type: ref

possible_keys: PRIMARY,idx_fk_film_id

key: idx_fk_film_id

key_len: 2

ref: sakila.film.film_id

rows: 2

Extra: Using index

*************************** 3. row ***************************

id: 1

select_type: SIMPLE

table: actor

type: eq_ref

possible_keys: PRIMARY

key: PRIMARY

key_len: 2

ref: sakila.film_actor.actor_id

rows: 1

Extra: NULL

3 rows in set (0.00 sec)

从上面的explain结果上看,mysql自动选择的关联顺序与使用straight_join关键字的关联顺序完全倒转过来,从上面的对比结果上可以看出,mysql把关联表倒转过来后,film只扫描一行,而film在最前时扫描行数是1000.

如果mysql选择首先扫描actor表,只会返回200条记录进行后面的嵌套循环查询,即,倒转关联顺序会让查询进行更少的嵌套循环和回溯操作,为了验证优化器的选择是否正确,我们单独执行了这两个查询,并且看last_query_cost状态值(这里我使用的虚拟机做测试),结果mysql选择的顺序预估成本为3883.340463,而我们手工指定的顺序预估成本为4012.731064。

这个简单的示例说明mysql是如何选项合适的关联顺序来让查询执行的成本尽可能低,重新定义关联的顺序是优化器非常重要的一部分,不过有时候,优化器给出的并不是最优的关联顺序,这时可以使    用straight_join关键字重写查询,让优化器按照你认为的最优的关联顺序执行,不过,人的判断很难那么精准,所以大部分时候,优化器做出的选择比普通人判断的要更精准。

关联优化器会尝试在所有的关联顺序中选择一个成本最小的来生成执行计划树,如果可能,优化器会遍历每一个表后逐个做嵌套循环计算每一颗可能的执行计划树的成本,最后返回一个最优的执行计划。

但是,糟糕的是,如果有超过n个表关联,那么需要检查n的阶乘种关联顺序,我们称之为所有可能的执行计划的“搜索空间”,搜索空间的增长速度非常快,如:若是10个关联,那么共有3628800种关联顺序,当搜索空间非常大的时候,优化器不可能逐一评估每一种关联顺序的成本,这时,优化器选择使用贪婪搜索的方式查找最优的关联顺序,实际上,当需要关联的表超过optimizer_search_depth的限制的时候,就会选择贪婪搜索模式了,optimizer_search_depth这个参数可以根据需要动态指定大小,默认为62。

 

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

  1. mysql(一) 关联查询的方式

    mysql做关联查询时,一般使用join....on.....的语法. 但还有其它两种语法形式,三者的主要区别在于书写形式,其余方面并无太多差异. 如下三种形式: select * from trad ...

  2. MySQL 查询优化 - 关联查询

    1. 关联查询执行流程 MySQL执行关联查询的策略很简单,他会从一个表中循环取出单条数据,然后用该条数据到下一个表中寻找匹配的行,然后回溯到上一个表,到所有的数据匹配完成为止.因此也被称为" ...

  3. 【mysql】关联查询_子查询_排序分组优化

    1. 关联查询优化 1.1 left join 结论: ①在优化关联查询时,只有在被驱动表上建立索引才有效! ②left join 时,左侧的为驱动表,右侧为被驱动表! 1.2 inner join ...

  4. MySQL 自关联查询

    定义表areas,结构如下 id atitle pid 因为省没有所属的省份,所以可以填写为null 城市所属的省份pid,填写省所对应的编号id 这就是自关联,表中的某一列,关联了这个表中的另外一列 ...

  5. Yii2实现跨mysql数据库关联查询排序功能

    遇到一个项目,需要跨表网上找了很多的资料,整理一下,方便以后再次使用 背景:在一个mysql服务器上(注意:两个数据库必须在同一个mysql服务器上)有两个数据库: memory (存储常规数据表) ...

  6. mysql的关联查询简写

    平常的内连接查询: SELECT * from ab_style as a INNER JOIN ab_url as b on a.style_bold=b.url_id 可支持简写风格: selec ...

  7. mysql 表关联查询报错 ERROR 1267 (HY000)

    解决翻案:http://stackoverflow.com/questions/1008287/illegal-mix-of-collations-mysql-error 即: SET collati ...

  8. MySQL执行一个查询的过程

    总体流程 客户端发送一条查询给服务器: 服务器先会检查查询缓存,如果命中了缓存,则立即返回存储在缓存中的结果.否则进入下一阶段: 服务器端进行SQL解析.预处理,再由优化器生成对应的执行计划: MyS ...

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

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

随机推荐

  1. JQuery 遍历子元素+ each函数的跳出+提取字符串中的数字

    最近脑袋迷糊的如同一团浆糊,一直出错. HTML代码如下图,现在想实现的功能是根据Ajax请求,获取到具体的button,以更新其样式.由于Button较多,每个Button都设置id,没有意义,想通 ...

  2. poj1700

    这题的最坑的地方就是每步可能会有两种情况,这两情况起初我都单独考虑了,但就是没放在一起考虑...wa个不停,果然贪心是一个很考验思维的东西. 这里可以这样考虑,在运人的过程中,河的起始岸最后终将剩下一 ...

  3. 条件查询,有input和select框,当查询条件获取焦点时支持摁下enter键查询

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  4. Log4J简单使用

    一.一般会将commons-logging和Log4j一起使用   原因:1.commons-logging功能较弱 2.log4j功能强大. 所需jar:       log4j-1.2.16.ja ...

  5. 房间安排-nyoj168

    描述 2010年上海世界博览会(Expo2010),是第41届世界博览会.于2010年5月1日至10月31日期间,在中国上海市举行.本次世博会也是由中国举办的首届世界博览会.上海世博会以“城市,让生活 ...

  6. array_reduce方法用回调函数迭代地将对数组的值进行操作

    在处理php数组的时候,有一种需求特别的频繁,如下二维数组: $arr = array( 1=>array( 'id' => 5, 'name' => '张三' ), 2=>a ...

  7. html 锚点的使用

    html 锚点 到底是干吗的?通俗简单地说,比如一篇很长的文章,你想按分段精确来看,那就可以用到锚点了. 代码:<a href="#001">跳到001</a&g ...

  8. ARM的一些基本概念

    MPU介绍: mpu是一个芯片,重力加速器(加速度)和陀螺仪(角速度) iic总线.在板上有iic控制器 连接着 最多128个外设,每个外设有地址,可以通信. 寄存器: cpu中的寄存器是为了加快运算 ...

  9. lsof 一切皆文件

    Docs » 工具参考篇 » 3. lsof 一切皆文件 Docs » 工具参考篇 » 3. lsof 一切皆文件 Edit on GitHub 3. lsof 一切皆文件¶ lsof(list op ...

  10. 开发回归测试之Selenium开发环境搭建

    前言 在解bug的过程中,经常遇到解完某个bug带来新的bug,原因一般是因为回归测试不足或不全面,下面我们来看看怎么利用selenium工具来完成开发后的回归测试, 虽然这可能是QA的事,不过有利于 ...