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 相关文章推荐
left join、inner join、right join的区别
Apr 05 MySQL
MySQL数字类型自增的坑
May 07 MySQL
解读MySQL的客户端和服务端协议
May 10 MySQL
Mysql基础知识点汇总
May 26 MySQL
Mysql效率优化定位较低sql的两种方式
May 26 MySQL
MySQL 常见存储引擎的优劣
Jun 02 MySQL
MySQL空间数据存储及函数
Sep 25 MySQL
MySQL利用UNION连接2个查询排序失效详解
Nov 20 MySQL
MySQL如何快速创建800w条测试数据表
Mar 17 MySQL
MySQL中一条SQL查询语句是如何执行的
Apr 08 MySQL
MySql重置root密码 --skip-grant-tables
Apr 11 MySQL
mysql函数之截取字符串的实现
Aug 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
php 中文字符入库或显示乱码问题的解决方法
2010/04/12 PHP
PHP根据IP地址获取所在城市具体实现
2013/11/27 PHP
PHP冒泡算法详解(递归实现)
2014/11/10 PHP
再推荐十款免费的php开发工具
2015/11/09 PHP
PHP实现权限管理功能示例
2017/09/22 PHP
PHP基于imagick扩展实现合成图片的两种方法【附imagick扩展下载】
2017/11/14 PHP
javascript KeyDown、KeyPress和KeyUp事件的区别与联系
2009/12/03 Javascript
JS跨域总结
2012/08/30 Javascript
图片轮换效果实现代码(点击按钮停止执行)
2013/04/12 Javascript
js获取时间(本周、本季度、本月..)
2013/11/22 Javascript
可编辑下拉框的2种实现方式
2014/06/13 Javascript
基于jQuery实现的美观星级评论打分组件代码
2015/10/30 Javascript
Windows 系统下设置Nodejs NPM全局路径
2016/04/26 NodeJs
简洁实用的BootStrap jQuery手风琴插件
2016/08/31 Javascript
更靠谱的H5横竖屏检测方法(js代码)
2016/09/13 Javascript
详解JavaScript中数组的reduce方法
2016/12/02 Javascript
react native仿微信PopupWindow效果的实例代码
2017/08/07 Javascript
修改UA在PC中访问只能在微信中打开的链接方法
2017/11/27 Javascript
vue中实现左右联动的效果
2018/06/22 Javascript
JavaScript查看代码运行效率console.time()与console.timeEnd()用法
2019/01/18 Javascript
解决小程序无法触发SESSION问题
2020/02/03 Javascript
Vue-cli 移动端布局和动画使用详解
2020/08/10 Javascript
Python使用win32 COM实现Excel的写入与保存功能示例
2018/05/03 Python
python 正确保留多位小数的实例
2018/07/16 Python
用Python将mysql数据导出成json的方法
2018/08/21 Python
python django model联合主键的例子
2019/08/06 Python
在pycharm中实现删除bookmark
2020/02/14 Python
Python pymsql模块的使用
2020/09/07 Python
澳洲小众品牌的集合网站:BNKR
2018/02/23 全球购物
时尚圣经:The Fashion Bible
2019/03/03 全球购物
有关花店创业的计划书模板
2019/08/27 职场文书
导游词之苏州寒山寺
2019/12/05 职场文书
python 批量压缩图片的脚本
2021/06/02 Python
mybatis中注解与xml配置的对应关系和对比分析
2021/08/04 Java/Android
根德5570型九灯四波段立体声收音机是电子管收音机的楷模 ? 再论5570
2022/04/05 无线电
Python作用域和名称空间的详细介绍
2022/04/13 Python