MySQL子查询的优化
本文基于MySQL5.7.19测试
创建四张表,pt1、pt2表加上主键
mysql> create table t1 (a1 int, b1 int);
mysql> create table t2 (a2 int, b2 int);
mysql> create table pt1 (a1 int, b1 int, primary key (a1));
mysql> create table pt2 (a2 int, b2 int, primary key (a2));
向表中分别插入10000条数据
mysql> delimiter //
mysql> create procedure prod_dt()
-> begin
-> declare i int;
-> set i=0;
-> while i<10000 do
-> insert into t1(a1,b1) values(i,i+1);
-> insert into t2(a2,b2) values(i+1,i+2);
-> insert into pt1(a1,b1) values(i,i+1);
-> insert into pt2(a2,b2) values(i+1,i+2);
-> set i=i+1;
-> end while;
-> end;
-> //
Query OK, 0 rows affected (0.05 sec) mysql> delimiter ;
mysql> call prod_dt() ;
MySQL支持对简单SELECT查询中的子查询优化,包括:
1 简单SELECT查询中的子查询。
2 带有DISTINCT、ORDERBY、LIMIT操作的简单SELECT查询中的子查询。
# 没有主键,优化器进行了优化,子查询物化后和表t1进行连接。执行计划中没有子查询
mysql> explain extended select * from t1 where t1.a1<100 and a1 in (select a2 from t2 where t2.a2 >10);
+----+--------------+-------------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+-------------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+
| 1 | SIMPLE | <subquery2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | 100.00 | Using where |
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 10362 | 3.33 | Using where; Using join buffer (Block Nested Loop) |
| 2 | MATERIALIZED | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 10157 | 33.33 | Using where |
+----+--------------+-------------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+
3 rows in set, 2 warnings (0.00 sec) #有主键,优化器进行了优化。执行计划中没有子查询
mysql> explain extended select * from pt1 where pt1.a1<100 and a1 in (select a2 from pt2 where pt2.a2 >10);
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------+------+----------+-------------+
| 1 | SIMPLE | pt1 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 89 | 100.00 | Using where |
| 1 | SIMPLE | pt2 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | abce.pt1.a1 | 1 | 100.00 | Using index |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------+------+----------+-------------+
2 rows in set, 2 warnings (0.00 sec) mysql>
MySQL不支持对如下情况的子查询进行优化:
-带有UNION操作。
-带有GROUPBY、HAVING、聚集函数。
-使用ORDERBY中带有LIMIT。
-内表、外表的个数超过MySQL支持的最大表的连接数。
#有聚合函数,没有进行子查询优化
mysql> explain extended select * from t1 where t1.a1>(select min(t2.a2) from t2);
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 10362 | 33.33 | Using where |
| 2 | SUBQUERY | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 10157 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
2 rows in set, 2 warnings (0.01 sec) mysql> explain extended select * from pt1 where pt1.a1>(select min(pt2.a2) from pt2);
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+------------------------------+
| 1 | PRIMARY | pt1 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 5000 | 100.00 | Using where |
| 2 | SUBQUERY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+------------------------------+
2 rows in set, 2 warnings (0.00 sec) mysql>
MySQL支持哪些子查询的优化技术?
1 子查询合并技术 --> 不支持
#t2表上执行了2次子查询。如果支持子查询合并技术,则t2表上只执行一次子查询
mysql> explain extended select * from t1 where a1<4 and (exists (select a2 from t2 where t2.a2<5 and t2.b2=1) or exists (select a2 from t2 where t2.a2<5 and t2.b2=2) );
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 10362 | 33.33 | Using where |
| 3 | SUBQUERY | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 10157 | 3.33 | Using where |
| 2 | SUBQUERY | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 10157 | 3.33 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
3 rows in set, 2 warnings (0.00 sec) mysql> #pt2表上执行了2次子查询。如果支持子查询合并技术,则pt2表上只执行一次子查询
mysql> explain extended select * from pt1 where a1<4 and (exists (select a2 from pt2 where pt2.a2<5 and pt2.b2=1) or exists (select a2 from pt2 where pt2.a2<5 and pt2.b2=2) );
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | PRIMARY | pt1 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 4 | 100.00 | Using where |
| 3 | SUBQUERY | pt2 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 4 | 10.00 | Using where |
| 2 | SUBQUERY | pt2 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 4 | 10.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
3 rows in set, 2 warnings (0.00 sec) mysql>
#人为的合并查询条件为“(t2.b2=1 OR t2.b2=2)”t2表上的子查询,只执行一次
mysql> explain extended select * from t1 where a1<10 and exists (select a2 from t2 where t2.a2<5 and (t2.b2=1 or t2.b2=2));
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 10362 | 33.33 | Using where |
| 2 | SUBQUERY | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 10157 | 6.33 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
2 rows in set, 2 warnings (0.01 sec) mysql> explain extended select * from pt1 where a1<10 and exists (select a2 from pt2 where pt2.a2<5 and (pt2.b2=1 or pt2.b2=2));
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | PRIMARY | pt1 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 10 | 100.00 | Using where |
| 2 | SUBQUERY | pt2 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 4 | 19.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
2 rows in set, 2 warnings (0.00 sec) mysql>
2 子查询展开(子查询反嵌套)技术 --> 支持得不够好
mysql> explain extended select * from t1, (select * from t2 where t2.a2 >10) v_t2 where t1.a1<10 and v_t2.a2<20;
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+
| 1 | SIMPLE | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 10157 | 11.11 | Using where |
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 10362 | 33.33 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+
2 rows in set, 2 warnings (0.00 sec) mysql> explain extended select * from pt1, (select * from pt2 where pt2.a2 >10) v_t2 where pt1.a1<10 and v_t2.a2<20;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | pt2 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 9 | 100.00 | Using where |
| 1 | SIMPLE | pt1 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 10 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 2 warnings (0.00 sec) mysql> #IN子查询的例子,可以看出子查询被物化
mysql> explain extended select * from t1 where t1.a1<100 and a1 in (select a2 from t2 where t2.a2 >10);
+----+--------------+-------------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+-------------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+
| 1 | SIMPLE | <subquery2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | 100.00 | Using where |
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 10362 | 3.33 | Using where; Using join buffer (Block Nested Loop) |
| 2 | MATERIALIZED | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 10157 | 33.33 | Using where |
+----+--------------+-------------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+
3 rows in set, 2 warnings (0.00 sec) mysql> #从查询执行计划看,子查询不存在,SQL语句被转换为内连接操作,这表明MySQL只有在针对主键列进行类似的子查询时,才把子查询上拉为内连接。所以,MySQL还是支持子查询展开技术的。
mysql> explain extended select * from pt1 where pt1.a1<100 and a1 in (select a2 from pt2 where pt2.a2 >10);
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------+------+----------+-------------+
| 1 | SIMPLE | pt1 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 89 | 100.00 | Using where |
| 1 | SIMPLE | pt2 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | abce.pt1.a1 | 1 | 100.00 | Using index |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------+------+----------+-------------+
2 rows in set, 2 warnings (0.00 sec) mysql>
3 聚集子查询消除技术 --> 不支持
#MySQL认为,聚集子查询,只需要执行一次,得到结果后,即可把结果缓冲到内存中供后续连接或过滤等操作使用,没有必要消除掉子查询。
#另外,如果聚集子查询在索引列上执行,则会更快得到查询结果,更加能加速查询速度。
mysql> explain extended select * from t1 where t1.a1>(select min(t2.a2) from t2);
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 10362 | 33.33 | Using where |
| 2 | SUBQUERY | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 10157 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
2 rows in set, 2 warnings (0.00 sec) mysql> explain extended select * from pt1 where pt1.a1>(select min(pt2.a2) from pt2);
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+------------------------------+
| 1 | PRIMARY | pt1 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 5000 | 100.00 | Using where |
| 2 | SUBQUERY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+------------------------------+
2 rows in set, 2 warnings (0.00 sec) mysql>
MySQL子查询的优化的更多相关文章
- [慢查优化]慎用MySQL子查询,尤其是看到DEPENDENT SUBQUERY标记时
案例梳理时间:2013-9-25 写在前面的话: 在慢查优化1和2里都反复强调过 explain 的重要性,但有时候肉眼看不出 explain 结果如何指导优化,这时候还需要有一些其他基础知识的佐助, ...
- MySQL 子查询(四)子查询的优化、将子查询重写为连接
MySQL 5.7 ref ——13.2.10.10优化子查询 十.子查询的优化 开发正在进行中,因此从长远来看,没有什么优化建议是可靠的.以下列表提供了一些您可能想要使用的有趣技巧.See also ...
- MySQL 子查询 EXISTS 和 NOT EXISTS(转)
MySQL EXISTS 和 NOT EXISTS 子查询 MySQL EXISTS 和 NOT EXISTS 子查询语法如下: SELECT ... FROM table WHERE EXISTS ...
- mysql子查询慢的问题
当你在用explain工具查看sql语句的运行计划时.若select_type 字段中出现"DEPENDENT SUBQUERY"时,你要注意了.你已经掉入了mysql子查询慢 ...
- MySQL子查询慢现象的解决
当你在用explain工具查看sql语句的执行计划时,若select_type 字段中出现“DEPENDENT SUBQUERY”时,你要注意了,你已经掉入了mysql子查询慢的“坑". 相 ...
- mysql数据库优化课程---16、mysql慢查询和优化表空间
mysql数据库优化课程---16.mysql慢查询和优化表空间 一.总结 一句话总结: a.慢查询的话找到存储慢查询的那个日志文件 b.优化表空间的话可以用optimize table sales; ...
- MySQL 子查询(一)
源自MySQL 5.7 官方手册 13.2.10 Subquery Syntax 〇.MySQL子查询介绍 子查询指的是嵌套在某个语句中的SELECT语句. MySQL支持标准SQL所要求的所有子查询 ...
- 第09章 MySQL子查询
第09章 MySQL子查询 子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从MySQL 4.1开始引入. SQL 中子查询的使用大大增强了 SELECT 查询的能力,因为很多时候查询需要 ...
- Mysql子查询、关联查询
mysql中update.delete.install尽量不要使用子查询 一.mysql查询的五种子句 where(条件查询).having(筛选).group by(分组).orde ...
随机推荐
- NVIDIA安装显卡提示你必须先安装Intel怎么办
无法安装驱动程序 计算机管理中查看当前的Intel的显卡驱动是否已经安装成功,如果显示的是标准VGA,则没有安装驱动,先要把Intel的显卡驱动装好(有时候360驱动大师这种工具并不能自动帮你装好 ...
- SQL还原后:目录名称无效
使用Sql Server备份文件,还原数据库出现如下错误:目录名称无效 解决方法:在系统临时文件夹内,如C:\Users\Administrator\AppData\Local\Temp\ 下新建名称 ...
- android中实现拨号功能
1.要实现拨号功能,首先需要开启拨号权限 修改AndroidManifest.xml文件,添加如下内容: <uses-permission android:name="android. ...
- iOS界面篇 - bounds和frame的相同和区别
相同点: 他们都是CGRect类型,且拥有属性origin(x, y), size(weight, height) 不同点: bounds是你画的视图的边界,和父视图没有半毛钱关系 frames则一 ...
- javascript数组操作大全,数组方法总汇
1. shift:删除原数组第一项,并返回删除元素的值:如果数组为空则返回undefined var a = [1,2,3,4,5]; var b = a.shift(); //a:[2,3,4,5] ...
- 高效率、简洁、CSS代码优化原则
高效率.简洁.CSS代码优化原则 CSS学起来并不难,但在大型项目中,一个团队中不同的人在书写CSS风格上也有不同这样这个项目就变得难以管理,团队上就更加难以沟通,为此总结了一些如何实现高效整洁的CS ...
- sqlalchemy结果转json
网上搜了下,http://blog.csdn.net/liu_xing_hui/article/details/8956107 介绍的很详细,自动一个Encoder给json的dump方法使用,能够实 ...
- 把文件(项目)上传到Git@OSC
说明: 登录与配置git的操作此处忽略. 假设你已配置好git,并且已绑定oschina. 下面是把一个本地文件夹上传到git的操作: 在git@osc上新建项目.记得选择添加对应的 .gitigno ...
- 【leetcode】solution in java——Easy1
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6409067.html 1:Hamming distance The Hamming distance betw ...
- OpenCV实现任意大小图片的合并(转)
OpenCV实现图像合并主要有两种方法 方法一:使用Mat.push_back方法将列数相同的图像加到最后一行 方法二: 主要思路是将图像拷贝到待合并图像的感兴趣区域 (1)新建一个要合并的图像(容器 ...