以下案例及写法来自 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 最值的优化 -- 利用分析函数改写相关推荐

  1. 利用分析函数改写范围判断自关联查询

    精彩预告:第八届数据技术嘉年华大会将于2018年11月16日~17日在北京市朝阳区东三环中路61号富力万丽酒店盛大开启.本次大会邀请互联网领先企业的数据库专家,国产数据库的领军人物,云技术等领域的知名 ...

  2. Oracle OR条件的优化与改写

    一. 同一字段谓词条件的or 1. 构造测试表 create table t_do as select * from dba_objects; create index t_do_n1 on t_do ...

  3. Oracle 数据库的性能优化

    oracle数据库的性能优化 对于ORACLE数据库的数据存取,主要有四个不同的调整级别,第一级调整是操作系统级包括硬件平台,第二级调整是ORACLE RDBMS级的调整,第三级是数据库设计级的调整, ...

  4. 【算法】基于hoare快速排序的三种思想和非递归,基准值选取优化【快速排序的深度剖析-超级详细的注释和解释】你真的完全学会快速排序了吗?

    文章目录 前言 什么是快速排序 快速排序的递归实现 快速排序的非递归实现 单趟排序详解 hoare思想 挖坑法 前后指针法 快速排序的优化 三数取中 小区间优化 快速排序整体代码 尾声 前言 先赞后看 ...

  5. Oracle SQL语句性能优化方法大全

    下面列举一些工作中常常会碰到的Oracle的SQL语句优化方法: 1.SQL语句尽量用大写的: 因为oracle总是先解析SQL语句,把小写的字母转换成大写的再执行. 2.选择最有效率的表名顺序(只在 ...

  6. Oracle]高效的SQL语句之分析函数

    [Oracle]高效的SQL语句之分析函数(一)--sum() 实际应用中我们可以通过sum()统计出组中的总计或者是累加值,具体示例如下: 1.创建演示表 create table emp as s ...

  7. oracle tps 性能,Oracle性能究极优化 下

    正在看的ORACLE教程是:Oracle性能究极优化 下. 我们有理由相信采用新的内核版本(2.2.16-3 smp)也应该有性能的提升: OS2: Newer minor version kerne ...

  8. oracle sql语句常用优化方法

    oracle sql语句常用优化方法 最近做一些报表查询,经常做一些小优化,在这里总结一下 语句上的优化: 1.SELECT 语句中避免使用*,用那些字段就摘出哪些. 2.SQL语句尽量用大写: 因为 ...

  9. oracle千万行update优化,Oracle的update优化

    Oracle的update语句优化研究 一. update语句的语法与原理 1. 语法 单表:UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值如:update t_join ...

最新文章

  1. wampserver环境配置局域网访问
  2. Linux查看内置命令和非内置命令帮助的几种方法(man、help、info)
  3. 洛谷 P2904 [USACO08MAR]跨河River Crossing
  4. JavaScript学习笔记之DOM篇,带你全面了解什么是DOM
  5. Backbone.js入门学习资源
  6. idea创建springboot项目+mybatis_从spring boot项目创建到netty项目过渡1
  7. [PhoneGap]Mac下搭建PhoneGap开发环境
  8. python贪心算法几个经典例子_贪心算法及示例,Python
  9. 运维:你们 JAVA 服务怎么又又又又出问题了,内存降不下来
  10. 银行中台与互联网中台有什么不同?该怎么建?
  11. Vivo手机安装谷歌Play商店,安装服务框架谷歌Google,支持X90,X80,X70,X60,s系列,IQOO
  12. 【敏捷】1.0 待办事项列表
  13. npm安装electron时报Error: EPERM: operation not permitted, scandir.....
  14. 更换内存条需要注意什么
  15. 学习Ansible自动化
  16. CISCO内网客户端软件anyconnect-win安装下载
  17. The Traitorous Eight in Semiconductor 半导体八叛逆
  18. iPhone X用户需要知道的12个隐藏小技巧 条条实用
  19. [CS229学习笔记] 5.判别学习算法与生成学习算法,高斯判别分析,朴素贝叶斯,垃圾邮件分类,拉普拉斯平滑
  20. 灰狼算法(GWO)优化混合核极限学习机(HKELM)分类预测,多输入单输出模型,GWO-HKELM分类预测。

热门文章

  1. Python Pygame动画原理
  2. TigerGraph | 图数据库建模最佳实践
  3. 【Hadoop离线基础总结】MapReduce增强(上)
  4. 微信公众平台开发OAuth2.0网页授权(转)
  5. 介绍Latex神器——Overleaf的方法
  6. 2-java安全——tomcat AJP协议文件包含分析[CVE-2020-1938]
  7. 记一次阿里云服务器因Redis被【挖矿病毒crypto和pnscan】攻击的case,附带解决方案
  8. elasticsearch数据类型--join
  9. AHP层次分析法python实现
  10. LayaBox上使用自定义WebAssembly初体验