MySQL中使用or、in与union all在查询命令下的效率对比


Posted in MySQL onMay 26, 2021

OR、in和union all 查询效率到底哪个快?
网上很多的声音都是说union all 快于 or、in,因为or、in会导致全表扫描,他们给出了很多的实例。
但真的union all真的快于or、in?

EXPLAIN SELECT * from employees where employees.first_NAME ='Georgi' UNION ALL SELECT * from employees where employees.first_NAME ='Bezalel'

这条语句执行结果481条,执行时间为0.35s

PRIMARY employees ALL 300141 Using where

UNION employees ALL 300141 Using where

 UNION RESULT <union1,2> ALL 

explain SELECT * FROM employees WHERE employees.first_name IN ('Georgi','Bezalel')

这条语句的执行结果时间为0.186s

SIMPLE  employees  ALL  300141  Using where

explain SELECT * FROM employees WHERE employees.first_name ='Georgi' or employees.first_name='Bezalel'

这条语句的执行结果和in的结果差不多

难道是网上的说法有误?难道和索引有关?在firstname上建立了一个索引

重新执行

union的执行执行计划如下,执行时间为0.004s

PRIMARY  employees  ref  index_firstname  index_firstname  44  const  253  Using where
UNION  employees  ref  index_firstname  index_firstname  44  const  228  Using where
UNION RESULT  <union1,2>  ALL

in的执行计划如下,执行时间也为0.004s

SIMPLE  employees  range  index_firstname  index_firstname  44  481  Using where

or的执行计划如下,执行时间也为0.004s

SIMPLE  employees  range  index_firstname  index_firstname  44  481  Using where

感觉性能差不多啊。但是注意执行计划中的type,ref要好于range哦(ref为非唯一性索引扫描,range为索引范围扫描)
突然感觉好像和网上说的差不多了,但是第一个语句走了两个ref扫描 会不会效率比走一次range的扫描低啊。

要不我再试试主键,这个是唯一的,会不会和网上的效果一直呢?

EXPLAIN SELECT * FROM employees WHERE employees.EMP_NO=100001 UNION ALL SELECT * FROM employees WHERE employees.EMP_NO=101100

union的执行计划如下

PRIMARY  employees  const  PRIMARY  PRIMARY  4  const  1  
UNION  employees  const  PRIMARY  PRIMARY  4  const  1  
UNION RESULT  <union1,2>  ALL  

EXPLAIN SELECT * FROM employees WHERE employees.EMP_NO IN (100001 ,101100)

in的执行计划如下

SIMPLE  employees  range  PRIMARY  PRIMARY  4  2  Using where

EXPLAIN SELECT * FROM employees WHERE employees.EMP_NO=100001 OR emp_no=101100

or的执行计划如下

SIMPLE  employees  range  PRIMARY  PRIMARY  4  2  Using where

感觉结果和第二个实验还是差不多。


下面本文就采用实例来探讨在实际的查询命令下它们之间的效率对比究竟如何。
1:创建表,插入数据、数据量为1千万【要不效果不明显】。

drop table if EXISTS BT; 
create table BT( 
  ID int(10) NOT NUll, 
  VName varchar(20) DEFAULT '' NOT NULL, 
  PRIMARY key( ID ) 
)ENGINE=INNODB;

 该表只有两个字段 ID为主键【索引页类似】,一个是普通的字段。(偷懒就用简单的表结构呢)
向BT表中插入1千万条数据
这里我写了一个简单的存储过程【所以你的mysql版本至少大于5.0,俺的版本为5.1】,代码如下。
注意:最好

INSERT INTO BT ( ID,VNAME ) VALUES( i, CONCAT( 'M', i ) );---1

    修改为

INSERT INTO BT ( ID,VNAME ) VALUES( i, CONCAT( 'M', i, 'TT' ) );---2

   修改原因在
   非索引列及VNAME使用了联合进行完全扫描请使用1 。
   非索引列及VNAME使用了全表扫描请使用2 。
 

DROP PROCEDURE IF EXISTS test_proc; 
CREATE PROCEDURE test_proc() 
BEGIN 
declare i int default 0; 
set autocommit = 0; 
while i<10000000 do 
INSERT INTO BT ( ID,VNAME ) VALUES( i, CONCAT( 'M', i ) ); 
set i = i+1; 
if i%2000 = 0 then 
commit; 
end if; 
end while; 
END;

 就不写注释呢,挺简单的。
存储过程是最好设置下innob的相关参数【主要和日志、写缓存相关这样能加快插入】,俺没有设置插入1千万条数据插了6分钟。
部分数据如下:1千万数据类似

MySQL中使用or、in与union all在查询命令下的效率对比

2:实战
    2.1 :分别在索引列上使用 or、in、union all
           我们创建的表只有主键索引,所以只能用ID做查询呢。我们查 ID 为 98,85220,9888589的三个数据各个耗时如下:

MySQL中使用or、in与union all在查询命令下的效率对比

时间都为0.00,怎么会这样呢,呵呵所有查询都是在毫秒级别。
我使用其他的工具--EMS SQL Manager  for mysql
查询显示时间为
93 ms, 94ms,93 ms,时间相差了多少几乎可以忽略。
然后我们在看看各自的执行计划

MySQL中使用or、in与union all在查询命令下的效率对比

这里要注意的字段type 与ref字段
我们发现union all 的所用的 type【type为显示连接使用了何种类型】 为ref 而or和in为range【ref连接类型优于range,相差不了多少】,而查询行数都一样【看rows字段都是为3】。
从整个的过程来看,在索引列使用常数or及in和union all查询相差不了多少。
但为什么在有的复杂查询中,再索引列使用or及in 比union all 速度慢很多呢,这可能是你的查询写的不够合理,让mysql放弃索引而进行全表扫描。
2.2:在非索引列中使用 or、in及union all。
    我们查 VNAME 为 M98,M85220,M9888589的三个数据各个耗时如下:

MySQL中使用or、in与union all在查询命令下的效率对比

我们发现为啥union all查询时间几乎为 or 和in的三倍。
这是为什么呢,我们先不说,先看看三个的查询计划。

MySQL中使用or、in与union all在查询命令下的效率对比

这里我们发现计划几乎一样。
但我们要注意扫描的此时对于 or及in 来说 只对表扫描一次即rows是列为9664782。
而对于union all 来说对表扫描了三次即rows的和为9664782*3。
这也是为什么我们看到union all 为几乎为三倍的原因。
备注: 如果使用存储过程使用第二sql该执行计划所有的type列 为 all,其实这个是我最想演示的,但现在已经快写完毕了才发现问题将错就错呢。

3:总结
     3.1:不要迷信union all 就比 or及in 快,要结合实际情况分析到底使用哪种情况。
     3.2:对于索引列来最好使用union all,因复杂的查询【包含运算等】将使or、in放弃索引而全表扫描,除非你能确定or、in会使用索引。
    3.3:对于只有非索引字段来说你就老老实实的用or 或者in,因为 非索引字段本来要全表扫描而union all 只成倍增加表扫描的次数。
    3.4:对于及有索引字段【索引字段有效】又包含非索引字段来时,按理你也使用or 、in或者union all 都可以,
       但是我推荐使用or、in。
      如以下查询:

select * from bt where bt.VName = 'M98' or bt.id ='9888589' 
 
select * from bt where bt.VName = 'M98' 
UNION ALL 
select * from bt where bt.id = '9888589'

     该两个查询速度相差多少 主要取决于 索引列查询时长,如索引列查询时间太长的话,那你也用or或者in代替吧。
   3.5: 以上主要针对的是单表,而多表联合查询来说,考虑的地方就比较多了,比如连接方式,查询表数据量分布、索引等,再结合单表的策略选择合适的关键字。 

MySQL 相关文章推荐
如何使用Maxwell实时同步mysql数据
Apr 08 MySQL
mysql查询的控制语句图文详解
Apr 11 MySQL
mysql left join快速转inner join的过程
Jun 30 MySQL
MySQL系列之十 MySQL事务隔离实现并发控制
Jul 02 MySQL
MySQL分区表实现按月份归类
Nov 01 MySQL
一文带你探究MySQL中的NULL
Nov 11 MySQL
MySQL 开窗函数
Feb 15 MySQL
MySQL数据库如何给表设置约束详解
Mar 13 MySQL
MySQL派生表联表查询实战过程
Mar 20 MySQL
MySQL 表锁定 LOCK和UNLOCK TABLES的 SQL语法
Apr 18 MySQL
MySQL数据库 任意ip连接方法
May 20 MySQL
MySQL详解进行JDBC编程与增删改查方法
Jun 16 MySQL
MySQL下使用Inplace和Online方式创建索引的教程
May 26 #MySQL
MySQL中InnoDB存储引擎的锁的基本使用教程
May 26 #MySQL
MySql存储过程之逻辑判断和条件控制
MYSQL主从数据库同步备份配置的方法
May 26 #MySQL
MYSQL数据库使用UTF-8中文编码乱码的解决办法
May 26 #MySQL
Mysql效率优化定位较低sql的两种方式
May 26 #MySQL
Mysql中 unique列插入重复值该怎么解决呢
May 26 #MySQL
You might like
PHP 字符串分割和比较
2009/10/06 PHP
PHP中文编码小技巧
2014/12/25 PHP
php+ajax实现无刷新动态加载数据技术
2015/04/28 PHP
PHP随机数 C扩展随机数
2016/05/04 PHP
PHP获取用户客户端真实IP的解决方案
2016/10/10 PHP
PHP实现针对日期,月数,天数,周数,小时,分,秒等的加减运算示例【基于strtotime】
2017/04/19 PHP
php+js实现点赞功能的示例详解
2020/08/07 PHP
js判断上传文件的类型和大小示例代码
2013/10/18 Javascript
jquery实现非叠加式的搜索框提示效果
2014/01/07 Javascript
DOM基础教程之模型中的模型节点
2015/01/19 Javascript
jQuery控制Div拖拽效果完整实例分析
2015/04/15 Javascript
JavaScript如何自定义trim方法
2015/07/28 Javascript
JavaScript图片轮播代码分享
2015/07/31 Javascript
在JavaScript的jQuery库中操作AJAX的方法讲解
2015/08/15 Javascript
jquery+html5烂漫爱心表白动画代码分享
2015/08/24 Javascript
快速解决jquery.touchSwipe左右滑动和垂直滚动条冲突
2016/04/15 Javascript
AngularJS ng-app 指令实例详解
2016/07/30 Javascript
JS中用try catch对代码运行的性能影响分析
2016/12/26 Javascript
为什么我们要做三份 Webpack 配置文件
2017/09/18 Javascript
微信小程序在ios下Echarts图表不能滑动的问题解决
2019/07/10 Javascript
vue控制多行文字展开收起的实现示例
2019/10/11 Javascript
JavaScript适配器模式原理与用法实例详解
2020/03/09 Javascript
[51:07]VGJ.S vs Pain 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
python 内置函数filter
2017/06/01 Python
Sanic框架基于类的视图用法示例
2018/07/18 Python
python实现图片九宫格分割
2021/03/07 Python
解决Python安装cryptography报错问题
2020/09/03 Python
Python判断变量是否是None写法代码实例
2020/10/09 Python
关于python tushare Tkinter构建的简单股票可视化查询系统(Beta v0.13)
2020/10/19 Python
python Gabor滤波器讲解
2020/10/26 Python
详解HTML5 Canvas绘制时指定颜色与透明度的方法
2016/03/25 HTML / CSS
用HTML5的canvas实现一个炫酷时钟效果
2016/05/20 HTML / CSS
艺术应用与设计个人的自我评价
2013/11/23 职场文书
上课迟到检讨书100字
2014/01/11 职场文书
卫生厅领导班子党的群众路线教育实践活动整改措施
2014/09/20 职场文书
Go语言-为什么返回值为接口类型,却返回结构体
2021/04/24 Golang