Oracle 最值的优化 -- 利用分析函数改写
以下案例及写法来自 triger liu《专题培训-SQL写法与改写》,有部分个人测试及删改,原文请参考原课程。
一、 最值在表关联中
1. 低效且有重复值的写法
找出各部门工资最高的员工信息
select a.EMPNO, a.ENAME, a.JOB, a.MGR, a.HIREDATE, a.SAL, a.COMM, a.DEPTNO
from emp a,
(select deptno, max(sal) as max_sal from emp group by deptno) b
where a.deptno = b.deptno
and a.sal = b.max_sal
order by DEPTNO
这个SQL有两个问题:
- 如果每个部门是最高工资的不止一个人,上面查询会有重复数据。如果每个部门最高工资只要取一个人,这个写法就是错的。
- emp 表扫描了两次,效率低
2. 高效且有重复值的写法
等价改写是使用max()分析函数,改写之后依然有重复值,但是emp表只扫描一次。如果确实是需要重复值的,就用这个方法改写。
select empno,ename,job,mgr,hiredate,sal,comm,deptno
from (select a.empno,a.ename,a.job,a.mgr,a.hiredate,a.comm,a.deptno,a.sal,
max(sal) over(partition by deptno) as max_sal
from emp a)
where sal = max_sal
order by deptno;
- 数据不去重
- emp表仅扫描一次
分析函数除了最常用的row_number,其实还有很多种,在不同场景下可以发挥妙用。
3. 高效且去重的写法
需要使用row_number分析函数
select *
from (select a.empno,a.ename,a.job,a.mgr,a.hiredate,a.comm,a.sal,a.deptno,
row_number() over(partition by deptno order by sal desc) as rn
from emp a)
where rn = 1
order by deptno
- 数据去重
- emp表只扫描一次
4. 实际案例
凌晨CPU炸裂,查看top cpu sql,发现就是类似的语句,单次执行超过4万秒
如果是想取各个product_code最大global_version的信息,这个写法会有重复的数据
可以用分析函数改写,最后这个order by不需要也可以不排
改写后1.5秒出结果,cpu消耗基本没有了
二、 最值在where条件中
还是找出各部门工资最高的员工信息,但我们这次换个写法
select a.empno,a.ename,a.job,a.mgr,a.hiredate,a.comm,sal,a.deptno
from emp a
where sal = (select max(sal)
from emp b
where b.deptno =a.deptno)
order by deptno;
注意蓝色部分虽然没有写group by,但它的实际含义就是按deptno分组取最大值,所以如果改成分析函数一定得记得加 partition by deptno
你会发现问题是一样的:
- 数据不去重
- emp表扫描两次
它的改写方法跟前面的一样的,这里不重复了
三、 取最值的最值
1. 测试表构造
create table TT_LOG(ID int,SYNC_DATE int,JOUR_NO int);
insert into TT_LOG values(333,1,4);
insert into TT_LOG values(333,2,3);
insert into TT_LOG values(333,2,2);
insert into TT_LOG values(666,2,5);
insert into TT_LOG values(666,2,3);
--插入些重复值
insert into TT_LOG values(666,3,4);
insert into TT_LOG values(666,3,4);
insert into TT_LOG values(666,3,2);
commit;
2. 原SQL
根据ID分组,取 SYNC_DATE 最大值,再取最大SYNC_DATE对应的最大 JOUR_NO。
SELECT B.ID, B.SYNC_DATE, B.JOUR_NO
FROM
(SELECT ID, MAX(SYNC_DATE) SYNC_DATE
FROM TT_LOG
GROUP BY ID
) A,
(SELECT ID, SYNC_DATE, MAX(JOUR_NO) JOUR_NO
FROM TT_LOG
GROUP BY ID, SYNC_DATE
) B
WHERE A.ID = B.ID
AND A.SYNC_DATE = B.SYNC_DATE;
上面的SQL虽然实现了功能,但扫描了两次 tt_log 表,效率较低。
3. 改写
事实上可以直接用分析函数实现该功能:根据ID分组,根据 SYNC_DATE和JOUR_NO 字段按分组排序,然后取最大值。
- 先看看取最大值前的数据是什么样的
SELECT ID,SYNC_DATE,JOUR_NO,
row_number() over(partition BY ID order by SYNC_DATE DESC,JOUR_NO DESC) rn
FROM TT_LOG;
对每个id值,按照 SYNC_DATE和JOUR_NO 倒序排序,并列出编号,其中RN=1的数据就是我们需要的。
- 加上取最大值
SELECT B.ID,B.SYNC_DATE,B.JOUR_NO
FROM
(SELECT ID,SYNC_DATE,JOUR_NO,
row_number() over(partition BY ID order by SYNC_DATE DESC,JOUR_NO DESC) rn
FROM TT_LOG
) B
WHERE rn=1;
数据正确,并且表只扫描了一次,更高效。
四、 一个复杂些的例子
1. 原sql
select count(*)
from jbpm_testtest1 t
join jbpm_testtest2 pi on t.procinst_=Pi.id_
inner join ccform_testtest3 d on d.cf_id = pi.id_
where ( t.end_= (select max(end_)
from jbpm_testtest1 s
where s.procinst_=d.cf_id
and d.cf_gdsta in ('K')
)
or (t.end_ is null and t.isopen =1 )
);
同样虽然求最值部分没有明着用group by,但因为它是在where条件中通过 procinst_ 字段关联,实际上就是对 procinst_ 字段分组求最值。
2. 比较直接的改法
直接把求max值部分提出作为临时表,然后改成关联(已经比原sql高效)
with V as
(
select procinst_,max(end_) as max_end_
from jbpm_testtest1
group by procinst_ -- 注意这里一定要有
)
select count(*)
from
( select t.procinst_,t.end_,t.isopen,d.cf_id,d.cf_gdsta,v.max_end_
from jbpm_testtest1 t
inner join jbpm_testtest2 pi
on t.procinst_=Pi.id_
inner join ccform_testtest3 d
on d.cf_id = pi.id_
left join v on
(d.cf_id=v.procinst_ and d.cf_gdsta in ('K'))
) where (end_ is null and isopen=1) or (end_ = max_end_ );
3. 改为分析函数
不要只想着把 jbpm_testtest1部分拆出来改为分析函数后再跟其他表关联,这里他们是一个整体。
- 所以第一步,先把它拆成 关联部分+条件部分
select count(*) from ( select xxxx from
from jbpm_testtest1 t
join jbpm_testtest2 pion t.procinst_=Pi.id_
inner join ccform_testtest3 don d.cf_id = pi.id_
) as tmp
where ( t.end_= (select max(end_)
from jbpm_testtest1 s
where s.procinst_=d.cf_id
and d.cf_gdsta in ('K')
)
- 第二步,把紫色部分看做一个整体,那xxxx部分应该是什么? —— 是where条件中的字段,所以把where条件中用到的字段挪上去
select count(*) from ( select t.procinst_,t.end_,t.isopen,d.cf_id,d.cf_gdsta,max_end
from jbpm_testtest1 t
join jbpm_testtest2 pion t.procinst_=Pi.id_
inner join ccform_testtest3 don d.cf_id = pi.id_
) as tmp
where ( t.end_= (select max(end_)
from jbpm_testtest1 s
where s.procinst_=d.cf_id
and d.cf_gdsta in ('K')
)
or (t.end_ is null and t.isopen =1 )
);
- 第三步,max_end 是怎么来的,就是通过分析函数,所以把这部分加上分析函数
select count(*) from ( select t.procinst_,t.end_,t.isopen,d.cf_id,d.cf_gdsta,
max(t.end_) over(partition by t.procinst_) as max_end
from jbpm_testtest1 t
join jbpm_testtest2 pion t.procinst_=Pi.id_
inner join ccform_testtest3 don d.cf_id = pi.id_
) as tmp
where ( t.end_= (select max(end_)
from jbpm_testtest1 s
where s.procinst_=d.cf_id
and d.cf_gdsta in ('K')
)
or (t.end_ is null and t.isopen =1 )
);
- 最后,既然已经有了max_end,where条件那个t.end=max(end_) 就可以替换掉了。当然,其他条件要保留
select count(*) from ( select t.procinst_,t.end_,t.isopen,d.cf_id,d.cf_gdsta,
max(t.end_) over(partition by t.procinst_) as max_end
from jbpm_testtest1 t
join jbpm_testtest2 pion t.procinst_=Pi.id_
inner join ccform_testtest3 don d.cf_id = pi.id_
) as tmp
where (end_= max_end and procinst_=d.cf_id and cf_gdsta in ('K'))
or (end_ is null and isopen =1);
最后结果
select count(*)
from
( select t.procinst_,t.end_ , t.isopen,d.cf_id,d.cf_gdsta,
max(end_) over (partition by t.procinst_) as max_end
from jbpm_testtest1 t
join jbpm_testtest2 pion t.procinst_=Pi.id_
inner join ccform_testtest3 don d.cf_id = pi.id_
) as tmp
where (end_=max_endand cf_id=procinst_ and cf_gdsta in ('K')) or (end_ is null and isopen=1) ;
参考
SQL优化经典----利用分析函数优化自连接_mengxiangfeiyang的专栏-CSDN博客
利用分析函数优化自连接_落落的专栏 专注SQL调优 性能调优-CSDN博客
用oracle分析函数优化自连接_kingtoon的专栏-CSDN博客
28 分析函数优化自连接,减少表访问次数 --优化主题系列_leo0805的博客-CSDN博客
第62篇-学习并分析“一次巧妙的SQL优化改写”
Oracle 最值的优化 -- 利用分析函数改写相关推荐
- 利用分析函数改写范围判断自关联查询
精彩预告:第八届数据技术嘉年华大会将于2018年11月16日~17日在北京市朝阳区东三环中路61号富力万丽酒店盛大开启.本次大会邀请互联网领先企业的数据库专家,国产数据库的领军人物,云技术等领域的知名 ...
- Oracle OR条件的优化与改写
一. 同一字段谓词条件的or 1. 构造测试表 create table t_do as select * from dba_objects; create index t_do_n1 on t_do ...
- Oracle 数据库的性能优化
oracle数据库的性能优化 对于ORACLE数据库的数据存取,主要有四个不同的调整级别,第一级调整是操作系统级包括硬件平台,第二级调整是ORACLE RDBMS级的调整,第三级是数据库设计级的调整, ...
- 【算法】基于hoare快速排序的三种思想和非递归,基准值选取优化【快速排序的深度剖析-超级详细的注释和解释】你真的完全学会快速排序了吗?
文章目录 前言 什么是快速排序 快速排序的递归实现 快速排序的非递归实现 单趟排序详解 hoare思想 挖坑法 前后指针法 快速排序的优化 三数取中 小区间优化 快速排序整体代码 尾声 前言 先赞后看 ...
- Oracle SQL语句性能优化方法大全
下面列举一些工作中常常会碰到的Oracle的SQL语句优化方法: 1.SQL语句尽量用大写的: 因为oracle总是先解析SQL语句,把小写的字母转换成大写的再执行. 2.选择最有效率的表名顺序(只在 ...
- Oracle]高效的SQL语句之分析函数
[Oracle]高效的SQL语句之分析函数(一)--sum() 实际应用中我们可以通过sum()统计出组中的总计或者是累加值,具体示例如下: 1.创建演示表 create table emp as s ...
- oracle tps 性能,Oracle性能究极优化 下
正在看的ORACLE教程是:Oracle性能究极优化 下. 我们有理由相信采用新的内核版本(2.2.16-3 smp)也应该有性能的提升: OS2: Newer minor version kerne ...
- oracle sql语句常用优化方法
oracle sql语句常用优化方法 最近做一些报表查询,经常做一些小优化,在这里总结一下 语句上的优化: 1.SELECT 语句中避免使用*,用那些字段就摘出哪些. 2.SQL语句尽量用大写: 因为 ...
- oracle千万行update优化,Oracle的update优化
Oracle的update语句优化研究 一. update语句的语法与原理 1. 语法 单表:UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值如:update t_join ...
最新文章
- wampserver环境配置局域网访问
- Linux查看内置命令和非内置命令帮助的几种方法(man、help、info)
- 洛谷 P2904 [USACO08MAR]跨河River Crossing
- JavaScript学习笔记之DOM篇,带你全面了解什么是DOM
- Backbone.js入门学习资源
- idea创建springboot项目+mybatis_从spring boot项目创建到netty项目过渡1
- [PhoneGap]Mac下搭建PhoneGap开发环境
- python贪心算法几个经典例子_贪心算法及示例,Python
- 运维:你们 JAVA 服务怎么又又又又出问题了,内存降不下来
- 银行中台与互联网中台有什么不同?该怎么建?
- Vivo手机安装谷歌Play商店,安装服务框架谷歌Google,支持X90,X80,X70,X60,s系列,IQOO
- 【敏捷】1.0 待办事项列表
- npm安装electron时报Error: EPERM: operation not permitted, scandir.....
- 更换内存条需要注意什么
- 学习Ansible自动化
- CISCO内网客户端软件anyconnect-win安装下载
- The Traitorous Eight in Semiconductor 半导体八叛逆
- iPhone X用户需要知道的12个隐藏小技巧 条条实用
- [CS229学习笔记] 5.判别学习算法与生成学习算法,高斯判别分析,朴素贝叶斯,垃圾邮件分类,拉普拉斯平滑
- 灰狼算法(GWO)优化混合核极限学习机(HKELM)分类预测,多输入单输出模型,GWO-HKELM分类预测。
热门文章
- Python Pygame动画原理
- TigerGraph | 图数据库建模最佳实践
- 【Hadoop离线基础总结】MapReduce增强(上)
- 微信公众平台开发OAuth2.0网页授权(转)
- 介绍Latex神器——Overleaf的方法
- 2-java安全——tomcat AJP协议文件包含分析[CVE-2020-1938]
- 记一次阿里云服务器因Redis被【挖矿病毒crypto和pnscan】攻击的case,附带解决方案
- elasticsearch数据类型--join
- AHP层次分析法python实现
- LayaBox上使用自定义WebAssembly初体验