mysql(4)—— 表连接查询与where后使用子查询的性能分析。
子查询就是在一条查询语句中还有其它的查询语句,主查询得到的结果依赖于子查询的结果。
子查询的子语句可以在一条sql语句的FROM,JOIN,和WHERE后面,本文主要针对在WHERE后面使用子查询与表连接查询的性能做出一点分析。
对于表连接查询和子查询性能的讨论众说纷纭,普遍认为的是表连接查询的性能要高于子查询。本文将从实验的角度,对这两种查询的性能做出验证,并就实验结果分析两种查询手段的执行流程对性能的影响。
首先准备两张表
1,访问日志表mm_log有150829条记录(相关sql文件已放在文章结尾的链接中)。

2,用户表mm_member有373条记录(相关sql文件已放在文章结尾的链接中)。

现在要求我们根据这两张表查出2017-02-06那天有那些用户登录过系统。
我们先来看一下使用表连接查询
SELECT
SQL_NO_CACHE mm.*
FROM
mm_member mm
JOIN
mm_log ml
ON
mm.id = ml.member_id
WHERE
ml.access_time LIKE '%2017-02-06%'
GROUP BY
ml.member_id;
这里使用了 SQL_NO_CACHE 是因为要多次执行这条sql语句,并计算出这条sql查询所耗费的平均时间,所以要关掉mysql的查询缓存,防止多次执行从缓存中读取数据。
mm.*是取GROUP BY ml.member_id分组后的诸多临时表的第一行数据,相关用法及原理请参见我的另一篇博客(http://www.cnblogs.com/cdf-opensource-007/p/6502556.html)
对这条sql语句执行了10次,查询所耗费的平均时间在0.120s左右。
查询结果:(一共有5个用户访问过系统)

至于以上这条sql的执行流程已经在前几篇博客中描述的很详细了,这里就不再做叙述了。
下面使用WHERE后使用子查询的方式实现
SELECT
SQL_NO_CACHE mm.username
FROM
mm_member mm
WHERE
mm.id IN(SELECT ml.member_id FROM mm_log ml WHERE ml.access_time LIKE '%2017-02-06%' GROUP BY ml.member_id);
当我第一次运行这条sql语句的时候,等了十几秒一直没有结果,我以为我的电脑死机,可Navicat显示处理中,最后40多秒的时候才运行出结果,接连运行了好多次在都是41秒左右出结果。

我们看到执行结果同上。那么使用子查询的性能到底低在哪里呢?
我的第一种推测是子语句:SELECT ml.member_id FROM mm_log ml WHERE ml.access_time LIKE '%2017-02-06%' GROUP BY ml.member_id耗费了大量的查询时间,因为mm_log这张表中有150829条记录。
把子语句单拿出来运行一下,发现子语句的运行时间也就在0.111s左右。
SELECT SQL_NO_CACHE member_id FROM mm_log ml WHERE ml.access_time LIKE '%2017-02-06%' GROUP BY ml.member_id;
这就说明我的第一种推测是不合理的。
那就分析下在WHERE后使用子查询IN的执行原理:
1,IN后面跟的子查询语句的执行结果只能有一列是用来和IN前面的主表的字段匹配的,在这里指的是mm.id。
2,一条带有子查询的sql语句首先执行的是子语句,主表的数据行按照IN前面主表的字段依次跟子查询的结果进行匹配,子查询中结果中有该数据行对应字段的值,则返回true,该行被WHERE筛选出来。没有则返回false,该行不被筛选。
3,那么按照2的说法,子查询的效率应该也不低啊,子语句的耗时在0.111s左右,而且主表mm_member和子语句的查询结果相匹配的次数,肯定是要少于表连接查询时数据行间匹配的次数的,但实验结果显示使用子查询的性能确实很低。
所以我有了第二种推测,主表mm_member数据行的每一行在与IN后面子语句的结果相匹配时,子语句都会重新执行一次,也就是说子语句第一次执行时,不会在内存中有缓存。这类似与使用了两个FOR循环嵌套,外层的FOR循环每拿出一个值,内层的FOR循环都要遍历一次。
那么根据以上的推测,拿主表mm_member的数据行数乘以子语句的执行时间就应该是整个查询的时间。
mm_member的数据行数:373
多次执行子语句算出平均时间在:0.111s
整个查询耗时的理论时间:41.403s
多次执行整个查询得出实际查询时间的平均值:40.834s
计算误差:(理论值-实际值)÷理论值 = 1.37%
误差还是在可以接受的范围内的,可以证明以上的推测。
根据以上的实验,我们可以得出的结论是,表连接查询的性能是要高于子查询的。
另外,对于在子查询中使用IN的性能高还是是用EXITS的性能高,有一种普遍的说法是:
1,在外表大,内表小,外表中有索引的情况下,使用IN。
2,在外表小,内表大,内表中有索引的情况下,使用EXITS。
先介绍一下EXITS的用法,刚好本例符合外表小内表大的情况,就以本例介绍一下。看下SQL:
SELECT
SQL_NO_CACHE mm.*
FROM
mm_member mm
WHERE
EXISTS(SELECT * FROM mm_log ml WHERE mm.id = ml.member_id AND ml.access_time LIKE '%2017-02-06%');
EXITS又简称代入查询,就是把主表的每一行代入子表的每一行进行检验,一旦子表中有符合的数据行就返回true,就可以取得主表中代入检验的那一行数据,否则返回false,不可以取得主表中代入检验的那一行数据。同IN不同的是,EXITS后的子语句不查询出结果,所以说SELECT后面的字段没有意义,一般使用*代替,由于EXITS的这种机制,当子表数据量比较大且有冗余字段的时候就很有可能避免了对子表的全表扫描,不像IN那样每次主表数据行来匹配都要进行全表扫描,并返回结果。所以说EXITS类似于两个FOR循环嵌套时,内层的FOR循环里面有 if(xxx){ break; }这种语法。
以上sql执行时间的平均在34秒左右,比使用IN要快上一些,但是跟表连接查询还不能比。
但是,在表与表之间没有关联关系时,就只能使用IN了。
sql 文件位置:http://pan.baidu.com/s/1gfLwIwr
最后说一点,我们作为程序员,研究问题还是要仔细深入一点的。当你对原理了解的有够透彻,开发起来也就得心应手了,很多开发中的问题和疑惑也就迎刃而解了,而且在面对其他问题的时候也可做到触类旁通。当然在开发中没有太多的时间让你去研究原理,开发中要以实现功能为前提,可等项目上线的后,你有大把的时间或者空余的时间,你大可去刨根问底,深入的去研究一项技术,为觉得这对一名程序员的成长是很重要的事情。
mysql(4)—— 表连接查询与where后使用子查询的性能分析。的更多相关文章
- 使用连接(JOIN)来代替子查询(Sub-Queries) mysql优化系列记录
		
使用连接(JOIN)来代替子查询(Sub-Queries) MySQL从 4.1开始支持SQL的子查询.这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查 ...
 - MySQL 子查询(四)子查询的优化、将子查询重写为连接
		
MySQL 5.7 ref ——13.2.10.10优化子查询 十.子查询的优化 开发正在进行中,因此从长远来看,没有什么优化建议是可靠的.以下列表提供了一些您可能想要使用的有趣技巧.See also ...
 - mysqlint类型的长度值mysql在建表的时候int类型后的长度代表什么
		
详解mysql int类型的长度值 mysql在建表的时候int类型后的长度代表什么 是该列允许存储值的最大宽度吗 为什么我设置成int(1), 也一样能存10,100,1000呢. 当时我虽然知道i ...
 - Mysql常用sql语句(19)- in / exists 子查询
		
测试必备的Mysql常用sql语句系列 https://www.cnblogs.com/poloyy/category/1683347.html 前言 子查询在我们查询方法中是比较常用的,通过子查询可 ...
 - 警惕 MySql 更新 sql 的 WHERE 从句中的 IN() 子查询时出现的性能陷阱
		
警惕 MySql 更新 sql 的 WHERE 从句中的 IN() 子查询时出现的性能陷阱 以下文章来源:https://blog.csdn.net/defonds/article/details/4 ...
 - 《MySQL必知必会学习笔记》:子查询
		
子查询 在開始了解子查询之前,首先做下准备工作,建立3个表, 一个是customers表,当中包含:客户名字.客户ID.客户Tel等. 一个是orders表,当中包含:订单号.客户ID.订单时间等. ...
 - mysql多表连接和子查询
		
文章实例的数据表,来自上一篇博客<mysql简单查询>:http://blog.csdn.net/zuiwuyuan/article/details/39349611 MYSQL的多表连接 ...
 - mysql多表连接查询
		
新建两张表: 表1:student 截图如下: 表2:course 截图如下: (此时这样建表只是为了演示连接SQL语句,当然实际开发中我们不会这样建表,实际开发中这两个表会有自己不同的主键.) ...
 - MySQL多表连接
		
主要分3种:内连接,外连接,交叉连接 其 他:联合连接,自然连接 1.内联接 典型的联接运算,使用像 = 或 <> 之类的比较运算).包括相等联接和自然联接. 内联接使用比 ...
 
随机推荐
- 【转载】从头编写 asp.net core 2.0 web api 基础框架 (3)
			
Github源码地址:https://github.com/solenovex/Building-asp.net-core-2-web-api-starter-template-from-scratc ...
 - JAVA中的 static使用
			
主要内容: 1.静态变量 2.静态方法 3.静态代码块 静态变量 我们知道,可以基于一个类创建多个该类的对象,每个对象都拥有自己的成员,互相独立.然而在某些时候,我们更希望该类所有的对象共享同一个成员 ...
 - poj 2758 && BZOJ 2258 Checking the Text 文本校对
			
Description 为了给Wind买生日礼物,Jiajia不得不找了一份检查文本的工作.这份工作很无聊:给你一段文本 要求比对从文本中某两个位置开始能匹配的最大长度是多少.但比无聊更糟糕的是, ...
 - BZOJ-USACO被虐记
			
bzoj上的usaco题目还是很好的(我被虐的很惨. 有必要总结整理一下. 1592: [Usaco2008 Feb]Making the Grade 路面修整 一开始没有想到离散化.然后离散化之后就 ...
 - IDEA的破解安装以及汉化
			
IDEA是一款比eclipse用起来更好用的一款代码编辑器,本人之前也是一直在用eclipse来写代码,后来发现了IDEA用起来会更顺手,所以又转用IDEA了,今天给大家分享一下IDEA的下载安装破解 ...
 - cesium编程入门(四)界面介绍及小控件隐藏
			
感性认识 界面介绍,viewer Geocoder : 查找位置工具,查找到之后会将镜头对准找到的地址,默认使用bing地图 Home Button :视角返回初始位置. Scene Mode Pic ...
 - Winform 控件的入门级使用(一)
			
开始总结一下控件的基本用法,方便以后查阅. 一.Label Label 的使用频率很高,基本上也没有什么难度. #region Winform //label label.Text = "这 ...
 - HDU 1874 畅通工程续(模板题——Floyd算法)
			
题目: 某省自从实行了很多年的畅通工程计划后,终于修建了很多路.不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离要短很多.这让行人很困扰 ...
 - asp.net -mvc框架复习(3)-控制器和动作方法的任务分析
			
using System;using System.Collections.Generic;using System.Linq;using System.Web;//ASP.NET核心命名空间usin ...
 - VIM 自定义语法高亮配置
			
VIM 没有自动支持语法高亮,自己动手搞一搞,网上查了一堆资料,终于配置成功, 记录一下,以便后期查看. 总共两步: step1 : 定义语法规则 1)需要创建一个定义语法规则的文件,这个 actio ...