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能够有效的提高系统的可用性. 总结的有点罗嗦,列个简单的目录啦~ 目录 第一部分知识准备 ...
随机推荐
- 为什么要使用ThreadLocalRandom代替Random生成随机数
799 java里有伪随机型和安全型两种随机数生成器,伪随机生成器根据特定公式将seed转换成新的伪随机数据的一部分,安全随机生成器在底层依赖到操作系统提供的随机事件来生成数据. 安全随机生成器 需要 ...
- 关于js中循环遍历中顺序执行多个嵌套ajax的问题
就是业务上需要完成一个功能,这个功能需要依次调用四个接口,后面接口的参数都依赖于前一个接口的返回值. 类似这样: var objArr = "从其他逻辑获得"; for(var n ...
- Oracle-08:连接查询
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 首先提供数据库脚本,供测试使用 create table DEPT ( deptno ) not null, ...
- Prefer ThreadLocalRandom over Random
Java 7 has introduced a new random number generator - ThreadLocalRandom Normally to generate Random ...
- Spring Cloud构建微服务架构(二)服务消费者
Netflix Ribbon is an Inter Process Communication (IPC) cloud library. Ribbon primarily provides clie ...
- thinter中lable标签控件(二)
lable控件 对于tkinter来说,学起来很简单,只要设置好相应的参数即可出结果,所以不用刻意去记住这些参数.学习一遍后理解每个参数的作用是什么即可. 当下次用到的时候来笔记上看一下就行. 内容很 ...
- python中用locust做简单的压力测试
验证导入包成功 首先导入locust模块,百度一下导入方法很多,就不多说了 验证导入成功或失败,在终端中(windows中是cmd)输入:locust --help 导入成功后会出现如下界面: 编辑运 ...
- js算法初窥04(算法模式01-递归)
终于来到了有点意思的地方--递归,在我最开始学习js的时候,基础课程的内容就包括递归,但是当时并不知道递归的真正意义和用处.我只是知道,哦...递归是自身调用自身,递归要记得有一个停止调用的条件.那时 ...
- BZOJ_1717_[Usaco2006 Dec]Milk Patterns 产奶的模式_后缀数组
BZOJ_1717_[Usaco2006 Dec]Milk Patterns 产奶的模式_后缀数组 Description 农夫John发现他的奶牛产奶的质量一直在变动.经过细致的调查,他发现:虽然他 ...
- js十大排序算法
排序算法说明: (1)对于评述算法优劣术语的说明 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面:不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面: 内排序:所有排 ...