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 相关文章推荐
浅谈mysql执行过程以及顺序
May 12 MySQL
一文读懂navicat for mysql基础知识
May 31 MySQL
MySQL深度分页(千万级数据量如何快速分页)
Jul 25 MySQL
mysql事务隔离级别详情
Oct 24 MySQL
MySQL窗口函数的具体使用
Nov 17 MySQL
全面盘点MySQL中的那些重要日志文件
Nov 27 MySQL
教你如何让spark sql写mysql的时候支持update操作
Feb 15 MySQL
Innodb存储引擎中的后台线程详解
Apr 03 MySQL
聊聊mysql都有哪几种分区方式
Apr 13 MySQL
MySQL中EXPLAIN语句及用法
May 20 MySQL
深入理解MySQL中MVCC与BufferPool缓存机制
May 25 MySQL
手把手带你彻底卸载MySQL数据库
Jun 14 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
虫族 Zerg 魔法科技
2020/03/14 星际争霸
PHP新手上路(八)
2006/10/09 PHP
PHP多文件上传实例
2015/07/09 PHP
php微信公众号开发之音乐信息
2018/10/20 PHP
学习ExtJS(一) 之基础前提
2009/10/07 Javascript
RequireJS入门一之实现第一个例子
2015/09/30 Javascript
JavaScript前端开发之实现二进制读写操作
2015/11/04 Javascript
jQuery实现二级下拉菜单效果
2016/01/05 Javascript
js 动态给元素添加、移除事件的实现方法
2016/07/19 Javascript
javascript 判断是否是微信浏览器的方法
2016/10/09 Javascript
微信小程序 教程之条件渲染
2016/10/18 Javascript
javascript中href和replace的比较(详解)
2016/11/25 Javascript
Angular.JS通过指令操作DOM的方法
2017/05/10 Javascript
玩转Koa之koa-router原理解析
2018/12/29 Javascript
详解Vue.js中引入图片路径的几种方式
2019/06/17 Javascript
vue通过video.js解决m3u8视频播放格式的方法
2019/07/30 Javascript
vue简单练习 桌面时钟的实现代码实例
2019/09/19 Javascript
javascript数组元素删除方法delete和splice解析
2019/12/09 Javascript
JS动态图片的实现方法完整示例
2020/01/13 Javascript
Python 可爱的大小写
2008/09/06 Python
简单谈谈Python中的反转字符串问题
2016/10/24 Python
EM算法的python实现的方法步骤
2018/01/02 Python
python 按不同维度求和,最值,均值的实例
2018/06/28 Python
Django框架安装方法图文详解
2019/11/04 Python
Django 批量插入数据的实现方法
2020/01/12 Python
python解决OpenCV在读取显示图片的时候闪退的问题
2021/02/23 Python
CSS Grid布局教程之网格单元格布局
2014/12/30 HTML / CSS
Pretty Little Thing爱尔兰:时尚女性服饰
2017/03/27 全球购物
粉红色的鲸鱼:Vineyard Vines
2018/02/17 全球购物
FragranceNet中文网:北美健康美容线上零售商
2020/08/26 全球购物
初中生物教学反思
2014/01/10 职场文书
教师通用专业自荐书范文
2014/02/11 职场文书
小学校本培训方案
2014/06/06 职场文书
资料员岗位职责
2015/02/10 职场文书
离婚起诉书范文2016
2015/11/26 职场文书
PostgreSQL并行计算算法及参数强制并行度设置方法
2022/04/07 PostgreSQL