本文同时发表在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. 《Linux命令行与shell脚本编程大全》第十五章 呈现数据

    15.1 理解输入和输出 现在知道两种显示脚本输出的方法 1)在显示器屏幕上显示 2)将输出文件重定向到文件中 15.1.1 标准文件描述符 Linux系统将每个对象当做文件处理.这包括输入和数出进程 ...

  2. 关于SpringBoot bean无法注入的问题(与文件包位置有关)改变自动扫描的包

    原因:同事在写demo时出现bean加了@component后却无法被spring扫描到(在编译的时候IDEA就提示拿不到对应的bean)的问题. 后来经过研究是跟文件包的位置有关的. springb ...

  3. mssql执行计划查看的一些知识

    在MSSQL中,查看较慢语句的执行计划,就是一个比较直观的方式, 如果查看执行计划呢: 1.从右到左,从上到下的顺序阅读执行计划2.执行计划中每个图标代表一个运算符,总开销为100%3.数据从右向左在 ...

  4. POJ2251-Dungeon Master

    题意:给出一三维空间的地牢,要求求出由字符'S'到字符'E'的最短路径移动方向可以是上,下,左,右,前,后,六个方向,每移动一次就耗费一分钟,要求输出最快的走出时间.不同L层的地图,相同RC坐标处是连 ...

  5. 一个web图片热点生成工具(winform开发) 附源码

    给图片加热点是web开发中经常用到的一个功能.这方面的工具也不少. 为了更好的满足自己的需求,写了一个winform程序. 可以方便的给图片加热点,更方便灵活! 源码下载 http://downloa ...

  6. 数据结构与算法(C/C++版)【栈与队列】

    第三章<栈与队列> (一)栈简介  栈(Stack):只允许在一端进行插入或删除操作的线性表.首先栈是一种线性表,但是限定这种线性表只能在某一端进行插入和删除操作栈顶(top):线性表允许 ...

  7. Mysql服务器SQL模式 (官方精译)

    MySQL服务器可以在不同的SQL模式下运行,并且可以根据sql_mode系统变量的值对不同的客户端应用不同的模式.DBA可以设置全局SQL模式以匹配站点服务器操作需求,并且每个应用程序可以将其会话S ...

  8. 给dalao们递dalao们的博客

    hqh使用0 1敲完AC代码 FNXF FNXF tfx .io结尾的都是dalao! ssttkkl 复读:.io结尾的都是dalao! ThetaS Pirote YanQuijote 昵称不能为 ...

  9. Linux多线程编程——线程的创建与退出

    POSIX线程标准:该标准定义了创建和操纵线程的一整套API.在类Unix操作系统(Unix.Linux.Mac OS X等)中,都使用Pthreads作为操作系统的线程.Windows操作系统也有其 ...

  10. C++彩色数据流动界面

    一个数据流动界面 #include <windows.h> #include <time.h> #include <cstdio> #include <str ...