oracle对查询结果求和_图解面试题:累计求和问题如何分析?

c7e8f6d50d76aa7e75e6c83ae12fa206.png

【题目】

“薪水表”中记录了员工发放的薪水。包含雇员编号,薪水、起始日期、结束日期。

其中,薪水是指该雇员在起始日期到结束日期这段时间内的薪水。当前员工是指结束日期 = '9999-01-01'的员工。

2872ccc096a4bc32d6a5bba8e965638d.png

业务问题:按照雇员编号升序排列,查找薪水的累计和(累计薪水)。其中累计薪水是前N个当前员工( 结束日期 = '9999-01-01')的薪水的累计和,其他以此类推。

b4bf1f67af6bf2ebb593605b98a8520c.png

【解题步骤】

1.先筛选出当前员工( 结束日期 = '9999-01-01')的薪水

select 雇员编号,薪水
from 薪水表
where 结束日期 = '9999-01-01';
select 雇员编号,薪水,
sum(薪水) over (order by 雇员编号) as 累计薪水
from 薪水表
where 结束日期 = '9999-01-01';

查询结果

ddaf2ab6b96811a467d67b2ea6bbf8a9.png

2.什么是累计薪水?

由题意可以看出输出结果需要包含薪水和累计薪水。累计薪水是前N个当前员工的薪水的累计和得出。

举个例子,如下图:

9fcd819335f73bfd9dcda5cfe2cc776b.png

第1行的累计薪水为雇员编号(10001)的薪水,

第2行的累计薪水为雇员编号(10001)、雇员编号(10002)的薪水之和,

第3行的累计薪水为雇员编号(10001)、雇员编号(10002)、雇员编号(10003)的薪水之和

依次类推...

3.如何计算出每行的累计薪水?

(1)方法1,用窗口函数(推荐)

在《猴子 从零学会SQL》里讲过窗口函数的基本语法如下:

<窗口函数> over (partition by <用于分组的列名>               
                order by <用于排序的列名>)

用聚合函数作为窗口函数,有累计的功能。因为本题是累计“求和”,所以用聚合函数sum。

select 雇员编号,薪水,
sum(薪水) over (order by 雇员编号) as 累计薪水
from 薪水表
where 结束日期 = '9999-01-01';

查询结果

b4bf1f67af6bf2ebb593605b98a8520c.png

(2)方法2,用自联结(不推荐)

“薪水表”中只有“雇员编号”和“薪水表”,根据上述累计薪水的计算方法,

3c657286a16334639a1c8c881f787ce8.png

因此我们需要得到下图所示的表1才能计算累计薪水,左边是雇员编号以及对应的当前薪水,右边则是左边雇员编号对应的求累计薪水需要用到的雇员编号和薪水。

如计算左边雇员编号10002的累计薪水则需用到右边雇员编号(1)中10001和10002两人的当前薪水,且需要满足右边雇员编号(1)<=左边雇员编号

19f722235edeabb0ea528e3554ee2c89.png

根据左边的雇员编号和薪水分组,再对右边的薪水(1)进行求和,即可得出每个雇员编号对应的累计薪水。

6c265fb1575dfee715d3e75db0743be9.png

那么,上述的表是如何得出的呢?薪水表中只有一列雇员编号和一列薪水,因此我们需要复制一张薪水表并与原来的合并,需要用到自联结,语法如下:

select 列名 
from 表名 as 别名1,表名 as 别名2;
select *
from 薪水表 as s1,薪水表 as s2;

206e4319f2460e1a92d0363ac4340812.png

需要加上什么条件吗?显然观察上述图表,需满足雇员编号(1)<=雇员编号,而题意当前员工的薪水需要满足结束日期 = '9999-01-01',并按雇员编号升序排列:

b924112a105a8614301e2e81ab1e607c.png
select s1.雇员编号,s1.薪水,s2.雇员编号,s2.薪水
from 薪水表 as s1,薪水表 as s2
where s2.雇员编号 <= s1.雇员编号 and s1.结束日期 = '9999-01-01' and s2.结束日期 = '9999-01-01'
order by s1.雇员编号;

301d47ccef8a5c43f985a74fb0e42855.png

最后用 group by 对雇员编号,薪水进行分组,并用 sum 函数对薪水(1)进行求和:

select s1.雇员编号,s1.薪水,sum(s2.薪水) as 累计薪水
from 薪水表 as s1,薪水表 as s2
where s2.雇员编号 <= s1.雇员编号 and s1.结束日期 = '9999-01-01' and s2.结束日期 = '9999-01-01'
group by s1.雇员编号,s1.薪水
order by s1.雇员编号;

8d0907e56dc54fd7687bab7cc9067ba0.png

【本题考点】

对于“累计”问题,要想到用聚合函数作为窗口函数。比如累计求和,用sum。

sum(列名) over (order by <用于排序的列名>)


累计求平均值,用avg。


avg(列名) over (order by <用于排序的列名>)


所以,我们可以得出“累计求和”问题的万能模板是:
select 列1,列2,
sum(列名) over (partition by <用于分组的列名>   
               order by <用于排序的列名>) as 累计值的别名
from 表名;

【举一反三】

下表为确诊人数表,包含日期和该日期对应的新增确诊人数

7d60cfe0ef0a8027296a6dc0ab3f5272.png

按照日期进行升序排列,查找日期、确诊人数以及对应的累计确诊人数。

参考答案:

select 日期,确诊人数,
sum(确诊人数) over (order by 日期) as 累计确诊人数
from 确诊人数表;

查询结果

fb689742857cb71cb5ba890c85d15a8d.png

f9c333dff64ab9053fcbde8a42044df1.png

推荐:如何从零学会sql?

fb6735bc0670e49195800532fca59713.png
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值