SQL优化--inner、left join替换in、not in、except
新系统上线,用户基数16万,各种查询timeout。打开砂锅问到底,直接看sql语句吧,都是泪呀,一大堆in\not in\except。这里总结一下,怎么替换掉in\not in\except。
1. in/except->left join
查询目的:
根据
- 客户表(Customer,按照站点、册本划分,16万数据)
- 水表表(Meter,16万数据)
- 水表抄表数据表(Meter_Data,远传表每天更新,27万数据)
关联查询,查询某天某个册本下水表未上传抄表数据的用户。
原查询结构
select *
from Customer cs
where
cs.Group_No = '册本编号' and
cs.Customer_No in
(
select Customer_No
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
where cs.Group_No = '册本编号'
except
select Customer_No
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
inner join Meter_data md on me.meter_no = md.meter_no and md.date = '2019-04-09'
where cs.Group_NO='册本编号'
)
原查询思路
- 查询出目标册本已上传数据的用户编号
select Customer_No
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
inner join Meter_data md on me.meter_no = md.meter_no and md.date = '2019-04-09'
where cs.Group_NO='册本编号'
- 查询出目标册本全部用户编号
select Customer_No
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
where cs.Group_No = '册本编号'
- 全部用户编号中排除已上传数据的用户编号,即为未上传数据的用户编号
全部用户编号 except 已抄表的用户编号
- 查询出在未抄表用户编号集合中的用户信息。
select *
from Customer cs
where
cs.Group_No = '册本编号' and
cs.Customer_No in
(全部用户编号 except 已抄表的用户编号)
思路倒是没有问题,但是in+except查询效率不要太慢了,本来想测试个时间,结果执行了几分钟愣是没出结果,直接终止掉了
优化查询结构
其实in\not in\except这些语法在查询中使用,效率不高是公认的事实,但是可能是由于语义比较明显吧,很多人还是喜欢这样用。我们这里使用left join来替代in+except。这里就来改掉上面的查询:
select cs.*
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
left join Meter_data md on me.meter_no = md.meter_no and md.date = '2019-04-09'
where cs.Group_NO='册本编号' and md.meter_no is null;
优化查询思路
- 用left join代替in+except,通过left join获取目标册本下全部用户的信息,并与当天上传的抄表数据进行连接;
- 连接中,右表为空即抄表数据为空的,即为当前未上传数据的客户信息;
left join on expression where expression 执行时,首先确保左表数据全部返回,然后应用on后指定的条件。因此,on的条件如果是对左表数据的过滤,是无效的;对右表数据的过滤是有效的。对左表数据的过滤条件,需要放到where条件中。
2. not in->left join
上面in+except的写法,可以使用not in简化一下,但是一样效率不高。这里想要说明的是not in也可以很方便的使用left join替换。
not in结构
select *
from Customer cs
where
cs.Group_No = '册本编号' and
cs.Customer_No not in
(
select Customer_No
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
inner join Meter_data md on me.meter_no = md.meter_no and md.date = '2019-04-09'
where cs.Group_NO='册本编号'
)
left join结构
select cs.*
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
left join Meter_data md on me.meter_no = md.meter_no and md.date = '2019-04-09'
where cs.Group_NO='册本编号' and md.meter_no is null;
3. in->inner join
查询目的
还是上面的查询背景,这里查询某天某个册本已经上传抄表数据的用户信息。
in结构
select *
from Customer cs
where
cs.Group_No = '册本编号' and
cs.Customer_No in
(
select Customer_No
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
inner join Meter_data md on me.meter_no = md.meter_no and md.date = '2019-04-09'
where cs.Group_NO='册本编号'
)
这里使用in不够高效,但是我们使用left join是否可以呢?
left join结构
select cs.*
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
left join Meter_data md on me.meter_no = md.meter_no and md.date = '2019-04-09'
where cs.Group_NO='册本编号' and md.meter_no is not null;
left join结构的话,这里需要使用is not null作为筛选条件。但是is not null同样非常低效。因此我们使用inner join
inner join结构
select cs.*
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
inner join Meter_data md on me.meter_no = md.meter_no and md.date = '2019-04-09'
where cs.Group_NO='册本编号';
inner join通过连接操作,直接获取到已上传抄表数据的用户信息。
4. not in -> in -> inner join
前面的查询场景中,我们默认的条件是未上传抄表数据的用户,当天在meter_data表是没有记录的。现在假设我们每天凌晨初始化meter_data表,设置抄表数值默认为零,抄表数据上传默认为state=0未上传。上传后,更新抄表数值和抄表状态state=1。
这时,我们来优化上面的not in查询结构还有另外一种思路。
not in结构
select *
from Customer cs
where
cs.Group_No = '册本编号' and
cs.Customer_No not in
(
select Customer_No
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
inner join Meter_data md on me.meter_no = md.meter_no and md.date = '2019-04-09'
where cs.Group_NO='册本编号' and meter.state=1
)
in结构
通过筛选条件取反,变换not in->in
select *
from Customer cs
where
cs.Group_No = '册本编号' and
cs.Customer_No in
(
select Customer_No
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
inner join Meter_data md on me.meter_no = md.meter_no and md.date = '2019-04-09'
where cs.Group_NO='册本编号' and meter.state=0
)
inner join结构
select cs.*
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
inner join Meter_data md on me.meter_no = md.meter_no and md.date = '2019-04-09'
where cs.Group_NO='册本编号' and meter.state=0;
5. 总结如下
上面的查询结构拆分出来后,大家可能觉得这么简单的sql怎么可能写成这个沙雕。其实真实业务系统,还有关联其他将近10张表。这里想说的是,在in\not in\except这种查询结构时,如果涉及到的数据量较大,建议坚决用连接替换。
- ... in (all except sub)... 查询结构可以转换为->left join
- ... not in ... 查询结构可以转换为->left join
- ... not in ... 查询也可以转换为 in -> inner join,这里需要确认转换查询条件时,是否有对应的数据
- ... in 查询结构可以转换为->inner join
SQL优化--inner、left join替换in、not in、except的更多相关文章
- sql优化 表连接join方式
sql优化核心 是数据库中 解析器+优化器的工作,我觉得主要有以下几个大方面:1>扫表的方法(索引非索引.主键非主键.书签查.索引下推)2>关联表的方法(三种),关键是内存如何利用 ...
- 022:SQL优化--JOIN算法
目录 一. SQL优化--JOIN算法 1.1. JOIN 写法对比 2. JOIN的成本 3. JOIN算法 3.1. simple nested loop join 3.2. index nest ...
- SQL 优化总结
SQL 优化总结 (一)SQL Server 关键的内置表.视图 1. sysobjects SELECT name as '函数名称',xtype as XType FROM s ...
- SQL优化大全
1. 优化SQL步骤 1. 通过 show status和应用特点了解各种 SQL的执行频率 通过 SHOW STATUS 可以提供服务器状态信息,也可以使用 mysqladmin extende d ...
- sql优化点整理
此文是我最早开始sql优化至今整理的小知识点和经常遇到的问题,弄懂这些对优化大型的sql会有不少帮助 ---------------------------------使用了多余的外连接------- ...
- 数据库SQL优化大总结之百万级数据库优化方案
网上关于SQL优化的教程很多,但是比较杂乱.近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充. 这篇文章我花费了大量的时间查找资料.修改.排版,希望大家阅读之后,感觉 ...
- (转)数据库SQL优化大总结之 百万级数据库优化方案
网上关于SQL优化的教程很多,但是比较杂乱.近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充. 这篇文章我花费了大量的时间查找资料.修改.排版,希望大家阅读之后,感觉 ...
- [03] SQL优化
1.SQL优化的实质 充分利用索引: 访问尽量少的数据块: 减少表扫描的I/O次数: 尽量避免全表扫描和其他额外开销: 2.oracle数据库常用的两种优化器 RBO(rule-based-optim ...
- sql优化(oracle)
系统优化中很重要的方面是SQL语句的优化,对于海量数据,优质的SQL能够有效的提高系统的可用性. 总结的有点罗嗦,列个简单的目录啦~ 目录 第一部分知识准备 ...
随机推荐
- 微信二维码支付-模式一(PC端,解决中文乱码问题)
近期公司调完银联,调支付宝,调完支付宝调微信.说实话微信的帮助文档确实是烂,而且有没有技术支持,害的我头发都掉了一桌.不说废话了,看代码. 首先登陆微信的公众平台(微信的服务号不是订阅号),然后选择微 ...
- 去重是distinct还是group by?
distinct简单来说就是用来去重的,而group by的设计目的则是用来聚合统计的,两者在能够实现的功能上有些相同之处,但应该仔细区分,因为用错场景的话,效率相差可以倍计. 单纯的去重操作使用di ...
- poi的合并单元格和冻结行列
//创建工作薄(excel) Workbook wb = new HSSFWorkbook(); //创建sheet Sheet createSheet = wb.createSheet(" ...
- SSM-SpringMVC-04:SpringMVC深入浅出理解HandleMapping(源码刨析)
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 先从概念理解,从中央调度器,携带参数request,调度到HandleMapping处理器映射器,处理器映射器 ...
- JQUERY获取loaded 宽高这么变态
JQUERY获取loaded 宽高这么变态: $('<img/>').attr('src',img.src).load(function() { img.Owidth = $(this). ...
- Linux时间子系统之(一):时间的基本概念
专题文档汇总目录 Notes:Linux时间基准点:Linux时间和broken-down time(struct tm):不同精度的时间表示time_t.timeval.timespec. 原文地址 ...
- Flask开发微电影网站(二)
1.安装数据库连接依赖包 pip install flask-sqlalchemy 2.创建movie数据库 在CentOS虚拟机,进入MaridDB数据库提示符,创建movie数据库 create ...
- JavaWeb(四)Servlet-1
1.Servlet 简介 Java Servlet是和平台无关的服务器端组件,它运行在Servlet容器中.Servlet容器负责Servlet和客户的通信以及调用Servlet的方法,Servlet ...
- input 去掉点击后出现的边框
添加属性 :focus{outline:none} 就可以去掉默认点击时,边框会出现的蓝色边框. :focus 选择器用于选取获得焦点的元素.提示:接收键盘事件或其他用户输入的元素都允许 :focus ...
- Hbase中HMaster作用
HMaster在功能上主要负责Table表和HRegion的管理工作,具体包括: 1.管理用户对Table表的增.删.改.查操作: 2.管理HRegion服务器的负载均衡,调整HRegion分布: 3 ...