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 25 MySQL
MySQL深度分页(千万级数据量如何快速分页)
Jul 25 MySQL
MySQL 用 limit 为什么会影响性能
Sep 15 MySQL
MySQL分库分表详情
Sep 25 MySQL
Mysql binlog日志文件过大的解决
Oct 05 MySQL
Mysql分库分表之后主键处理的几种方法
Feb 15 MySQL
一次Mysql update sql不当引起的生产故障记录
Apr 01 MySQL
MySQL时区造成时差问题
Apr 13 MySQL
MySQL 字符集 character
May 04 MySQL
MySQL普通表如何转换成分区表
May 30 MySQL
MySql中的json_extract函数处理json字段详情
Jun 05 MySQL
SQL语句中EXISTS的详细用法大全
Jun 25 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中全面阻止SQL注入式攻击分析小结
2012/01/30 PHP
深入php list()函数的详解
2013/06/05 PHP
使用PHP开发留言板功能
2019/11/19 PHP
JavaScript中Array 对象相关的几个方法
2006/12/22 Javascript
JavaScript中String和StringBuffer的速度之争
2010/04/01 Javascript
JavaScript数据结构和算法之图和图算法
2015/02/11 Javascript
深入理解JavaScript中的对象
2015/06/04 Javascript
js中 计算两个日期间的工作日的简单实例
2016/08/08 Javascript
JS实现HTML标签转义及反转义
2020/04/14 Javascript
详解jQuery事件
2017/01/13 Javascript
JavaScript基本类型值-Number类型
2017/02/24 Javascript
Vue.js如何优雅的进行form validation
2017/04/07 Javascript
React中使用collections时key的重要性详解
2017/08/07 Javascript
javaScript封装的各种写法
2017/08/14 Javascript
基于vue-cli、elementUI的Vue超简单入门小例子(推荐)
2019/04/17 Javascript
详解关闭令人抓狂的ESlint 语法检测配置方法
2019/10/28 Javascript
[05:59]带你看看DPC的台前幕后
2021/03/11 DOTA
讲解Python中的标识运算符
2015/05/14 Python
使用Python爬了4400条淘宝商品数据,竟发现了这些“潜规则”
2018/03/23 Python
python 通过麦克风录音 生成wav文件的方法
2019/01/09 Python
numpy数组广播的机制
2019/07/12 Python
python 实现dict转json并保存文件
2019/12/05 Python
python Paramiko使用示例
2020/09/21 Python
详解vscode实现远程linux服务器上Python开发
2020/11/10 Python
selenium学习教程之定位以及切换frame(iframe)
2021/01/04 Python
印度尼西亚在线时尚购物网站:ZALORA印尼
2016/08/02 全球购物
SmartBuyGlasses德国:购买太阳镜和眼镜
2019/08/20 全球购物
捐书寄语赠言
2014/01/18 职场文书
法人授权委托书范本
2014/04/04 职场文书
人代会标语
2014/06/30 职场文书
终止劳动合同协议书
2014/10/05 职场文书
实习证明格式范文
2014/10/14 职场文书
倡议书范文大全
2015/04/28 职场文书
党支部考察鉴定意见
2015/06/02 职场文书
导游词之阆中古城
2019/12/23 职场文书
Pytorch 实现变量类型转换
2021/05/17 Python