新系统上线,用户基数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='册本编号'
)

原查询思路

  1. 查询出目标册本已上传数据的用户编号
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='册本编号'
  1. 查询出目标册本全部用户编号
select Customer_No
from Customer cs
left join Meter me on cs.Customer_No = me.Customer_No
where cs.Group_No = '册本编号'
  1. 全部用户编号中排除已上传数据的用户编号,即为未上传数据的用户编号
全部用户编号 except 已抄表的用户编号
  1. 查询出在未抄表用户编号集合中的用户信息。
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;

优化查询思路

  1. 用left join代替in+except,通过left join获取目标册本下全部用户的信息,并与当天上传的抄表数据进行连接;
  2. 连接中,右表为空即抄表数据为空的,即为当前未上传数据的客户信息;

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的更多相关文章

  1. sql优化 表连接join方式

        sql优化核心 是数据库中 解析器+优化器的工作,我觉得主要有以下几个大方面:1>扫表的方法(索引非索引.主键非主键.书签查.索引下推)2>关联表的方法(三种),关键是内存如何利用 ...

  2. 022:SQL优化--JOIN算法

    目录 一. SQL优化--JOIN算法 1.1. JOIN 写法对比 2. JOIN的成本 3. JOIN算法 3.1. simple nested loop join 3.2. index nest ...

  3. SQL 优化总结

    SQL 优化总结 (一)SQL Server 关键的内置表.视图 1. sysobjects         SELECT name as '函数名称',xtype as XType  FROM  s ...

  4. SQL优化大全

    1. 优化SQL步骤 1. 通过 show status和应用特点了解各种 SQL的执行频率 通过 SHOW STATUS 可以提供服务器状态信息,也可以使用 mysqladmin extende d ...

  5. sql优化点整理

    此文是我最早开始sql优化至今整理的小知识点和经常遇到的问题,弄懂这些对优化大型的sql会有不少帮助 ---------------------------------使用了多余的外连接------- ...

  6. 数据库SQL优化大总结之百万级数据库优化方案

    网上关于SQL优化的教程很多,但是比较杂乱.近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充. 这篇文章我花费了大量的时间查找资料.修改.排版,希望大家阅读之后,感觉 ...

  7. (转)数据库SQL优化大总结之 百万级数据库优化方案

    网上关于SQL优化的教程很多,但是比较杂乱.近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充. 这篇文章我花费了大量的时间查找资料.修改.排版,希望大家阅读之后,感觉 ...

  8. [03] SQL优化

    1.SQL优化的实质 充分利用索引: 访问尽量少的数据块: 减少表扫描的I/O次数: 尽量避免全表扫描和其他额外开销: 2.oracle数据库常用的两种优化器 RBO(rule-based-optim ...

  9. sql优化(oracle)

    系统优化中很重要的方面是SQL语句的优化,对于海量数据,优质的SQL能够有效的提高系统的可用性. 总结的有点罗嗦,列个简单的目录啦~ 目录 第一部分知识准备                      ...

随机推荐

  1. eclipse 创建maven web示例

    注意,以下所有需要建立在你的eclipse等已经集成配置好了maven了,没有的话需要安装maven. 一.创建项目 1.新建maven项目,如果不在上面,请到other里面去找一下 一直点击下一步, ...

  2. Java bean和json互转时,屏蔽某个属性

    有的时候我们把java bean 转换成json的时候,希望屏蔽掉某个属性,这时可以在java bean的属性上加上@JsonIgnore注解,在com.fasterxml.jackson.annot ...

  3. 深入理解SpringBoot之装配条件

    我们知道自动装配是SpringBoot微服务化的核心,它会把META-INF/spring.factoires里配置的EnableAutoConfiguration注册到IOC容器里.但是,请大家考虑 ...

  4. Python实现猜数字游戏1.0版

    本文由荒原之梦原创,原文链接:http://zhaokaifeng.com/?p=702 """ 功能: 随机生成一个数字,最多有3次猜测机会,如果第一次没有猜对,则从第 ...

  5. [ Java面试题 ]泛型篇

    1.Java中的泛型是什么 ? 使用泛型的好处是什么? 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数. 好处: 1.类型安全,提供编译期间的类 ...

  6. 【数据结构】B-Tree, B+Tree, B*树介绍

    [摘要] 最近在看Mysql的存储引擎中索引的优化,神马是索引,支持啥索引.全是浮云,目前Mysql的MyISAM和InnoDB都支持B-Tree索引,InnoDB还支持B+Tree索引,Memory ...

  7. knockout + easyui = koeasyui

    在做后台管理系统的同学们,是否有用easyui的经历.虽然现在都是vue.ng.react的时代.但easyui(也就是jquery为基础)还是占有一席之地的.因为他对后端开发者太友好了,太熟悉不过了 ...

  8. .Net Core vs .Net Framework 如何为一个应用程序选择一个运行时

    .Net Core是下一件大事吗?我已经使用了一段时间了,我倾向认为它是.事实上,我们推测,在2018年,对这项技术熟练的开发人员将会有巨大的需求.但是它和.Net Framework的区别是什么?你 ...

  9. SQL注入简单介绍

    一.SQL注入概念   1.sql注入是一种将sql代码添加到输入参数中   2.传递到sql服务器解析并执行的一种攻击手法   举例:某个网站的用户名为name=‘admin’.执行时为select ...

  10. python基础下的mysql学习之操作命令(一)

    E-R模型简介: 该模型应用于数据库设计中的概念结构设计阶段 E-entry,实体,是指设计的主体.对象等: R-relationship,关系,是指设计的主体之间的联系,关系也是一种数据: 一个实体 ...