PL/SQL识趣--巧用Over函数

学习是枯燥的,所以作为一个学习者,要学会在学习中找到快乐,这样才能激发兴趣,兴趣是最好的老师,这样,学习就慢慢的变成了一件快乐的事情叻。原本一直做Oracle的管理和维护,对PLSQL不是很感兴趣,在CSDN里经常有些朋友问到开发的问题,开始是通过google找到答案,后来在找寻的过程中,学习到了知识点,也就慢慢的开始帮助别人进行sql的开发帮助,这种帮助也给我了学习的动力,所以PLSQL的开发的基本东西也就掌握了不少了,而且由于是快乐的学习,所以也比较轻松,这里就是用个一个网友的问题的完整解题过程来看看我当时对Over的学习。

这是一个比较有趣的SQL题, 


有一个表,结构如下: 
编号,姓名,时间,基本工资,奖金,扣款 
1 张三 8-20 1000 1000 5 
2 李四 8-20 1100 900 10 
3 张三 9-20 900 1000 10 
4 李四 9-20 1100 900 13 
5 王五 9-20 900 100 0 

如果我对8-20 和 9-20 的基本工资做异动数据查询 我要求得到这样的一个临时数据视图 

编号,姓名,基本工资 备注 
1 张三 -100 
2 王五 900 新增 


原帖在http://topic.csdn.net/u/20090830/20/4f135d40-a52d-4346-ac15-599b2951af39.html?seed=709316630&r=59447116#r_59447116  

遇到有趣的问题,一下就激发我的兴趣,看这样需要上下记录对照的,就想到lag和lead函数了

lag和lead的功能是Oracle数据库独有的功能,是分组函数中的一个很有价值的方法。

这里也借这个题,好好的解释一下lag和lead的用法 

Lag 取前面n条的记录 


lag(field, n, defaultv) 

这里field是我们需要取的字段, n是取多少个,defaultV是取不到的默认值。



Lead取后面那条的记录 
lead(field, n, defaultv) 

和lag一样的,field是取值的字段, n是前后几条, defualtv是默认值,类似nvl(lag(field, n), defaultv) 

lag和lead必须和开窗函数over一起来使用, 

那么知道Oracle里有这样实用的功能,那么我们如何具体来使用lag/lead和over,实战胜于一切 

先做数据 
现在习惯用with as来做数据了 

with temp as (select '张三' name, '8-20' date1, 1000 a1, 1000 a2, 5 a3 from dual union all select '李四' name, '8-20' date1, 1100 a1, 900 a2, 10 a3 from dual union all select '张三' name, '9-20' date1, 900 a1, 1000 a2, 10 a3 from dual union all select '李四' name, '9-20' date1, 1100 a1, 900 a2, 13 a3 from dual union all select '王五' name, '9-20' date1, 900 a1, 100 a2, 0 a3 from dual)

数据如下,建立临时的视图。 

下面是步骤,这里需要前后记录做比对,从而算出异动值,同时有可能只有一条记录的,时间后-时间前,所以用date1排序desc, 

刚才说到了,lag, lead都是分组的方法,必须和开窗Over函数一起用,

Over函数是Oracle里的开窗函数,功能相当的强大,这个类似一个时间之窗,以指定的字段进行group by,也就是这里所说到的类似一个值为维度的时间之窗。 这里指定维度字段用partition by关键字,这样就是以这个partition by后面的字段作为分组的依据,形成这个时间之窗。 我们还可以在维度里,指定另一个维度的排序。

结合着,我们这里想要的结果,我们可以看到需要用name做这个维度。

写法如下。

以user为单位,所以需要按name来partition 

如下 
over(partition by name order by date1 desc)


select name,a1,date1, a1-(lead(a1, 1, 0) over(partition by name order by date1 desc)) c from temp; NAME A1 DATE C ---- ---------- ---- ---------- -- 李四 1100 9-20 0 李四 1100 8-20 1100 王五 900 9-20 900 张三 900 9-20 -100 张三 1000 8-20 1000  



这里执行的结果如下,现在异动数据出来了,

但是,多余的记录要去掉,怎么去掉多余的记录了。 

其实这里两天记录我们只用第一条就可以了。row_number就可以搞定哟。 

继续 

select name,a1,date1, a1-(lead(a1, 1, 0) over(partition by name order by date1 desc)) c, row_number() over(partition by name order by date1 desc) rn from temp NAME A1 DATE C RN
 

---- ---------- ---- ---------- ---------- -- 

李四 1100 9-20 0 1 

李四 1100 8-20 1100 2 

王五 900 9-20 900 1 

张三 900 9-20 -100 1 

张三 1000 8-20 1000 2 


用rn和c=0过滤就可以了,变化为 


select name "姓名", c "基本工资" from ( 

select name,a1,date1, a1-(lead(a1, 1, 0) over(partition by name order by date1 desc)) c, row_number() over(partition by name order by date1 desc) rn from temp) where rn = 1 and c <>0 


姓名 基本工资 

---- ---------- 

王五 900 

张三 -100 


下面该处理备注了,备注的应该是只有一条记录的,用sum或者count就可以了 

sum(1) over(partition by name) 这里要特别注意 开窗函数的range的问题。不能用order by在over里,否则默认的开窗的range不是full range,而是current position。 

sum(1) over(partition by name order by date1) 


差别 

NAME A1 DATE C RN CNT 

---- ---------- ---- ---------- ---------- ---------- 

李四 1100 9-20 0 1 1 

李四 1100 8-20 1100 2 2 

王五 900 9-20 900 1 1 

张三 900 9-20 -100 1 1 

张三 1000 8-20 1000 2 2 



sum(1) over(partition by name) 

NAME A1 DATE C RN CNT 

---- ---------- ---- ---------- ---------- ---------- 

李四 1100 9-20 0 1 2 

李四 1100 8-20 1100 2 2 

王五 900 9-20 900 1 1 

张三 900 9-20 -100 1 2 

张三 1000 8-20 1000 2 2 

主要是cnt的差别,这是over开窗的一个特性,很多不熟悉的朋友经常这里不知何为了。 

去掉order by使用full range 

最后的效果如下 

select name "姓名", c "基本工资", decode(cnt, 1, '新增', '') "备注" from (select name,a1,date1, a1-(lead(a1, 1, 0) over(partition by name order by date1 desc)) c, row_number() over(partition by name order by date1 desc) rn, sum(1) over(partition by name) cnt from temp) where rn = 1 and c<>0; 

姓名 基本工资 备注 

---- ---------- ---- 

王五 900 新增 

张三 -100 

这里的题很有趣,也带出了lag lead over这样功能强大的函数。 

这里还合理的利用了over的range特性,啊,做这样的题,自己也受益不浅哟,快哉,快哉。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

inthirties

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值