圖解面試題:累計求和問題如何分析?
【題目】
“薪水錶”中記錄了員工發放的薪水。包含僱員編號,薪水、起始日期、結束日期。
其中,薪水是指該僱員在起始日期到結束日期這段時間內的薪水。當前員工是指結束日期 = '9999-01-01'的員工。
業務問題:按照僱員編號升序排列,查詢薪水的累計和(累計薪水)。其中累計薪水是前N個當前員工(結束日期 = '9999-01-01')的薪水的累計和,其他以此類推。
【解題步驟】
1.先篩選出當前員工(結束日期= '9999-01-01')的薪水
select 僱員編號,薪水
from 薪水錶
where 結束日期 = '9999-01-01';
查詢結果
2.什麼是累計薪水?
由題意可以看出輸出結果需要包含薪水和累計薪水。累計薪水是前N個當前員工的薪水的累計和得出。
舉個例子,如下圖:
第1行的累計薪水為僱員編號(10001)的薪水,
第2行的累計薪水為僱員編號(10001)、僱員編號(10002)的薪水之和,
第3行的累計薪水為僱員編號(10001)、僱員編號(10002)、僱員編號(10003)的薪水之和
依次類推...
3.如何計算出每行的累計薪水?
(1)方法1,用視窗函式(推薦)
在《猴子 從零學會SQL》裡講過視窗函式的基本語法如下:
<視窗函式> over (partition by <用於分組的列名>
orderby<用於排序的列名>)
用聚合函式作為視窗函式,有累計的功能。因為本題是累計“求和”,所以用聚合函式sum。
select 僱員編號,薪水,
sum(薪水) over (order by 僱員編號) as 累計薪水
from 薪水錶
where 結束日期 = '9999-01-01';
查詢結果
(2)方法2,用自聯結(不推薦)
“薪水錶”中只有“僱員編號”和“薪水錶”,根據上述累計薪水的計算方法,
因此我們需要得到下圖所示的表1才能計算累計薪水,左邊是僱員編號以及對應的當前薪水,右邊則是左邊僱員編號對應的求累計薪水需要用到的僱員編號和薪水。
如計算左邊僱員編號10002的累計薪水則需用到右邊僱員編號(1)中10001和10002兩人的當前薪水,且需要滿足右邊僱員編號(1)<=左邊僱員編號
根據左邊的僱員編號和薪水分組,再對右邊的薪水(1)進行求和,即可得出每個僱員編號對應的累計薪水。
那麼,上述的表是如何得出的呢?薪水錶中只有一列僱員編號和一列薪水,因此我們需要複製一張薪水錶並與原來的合併,需要用到自聯結,語法如下:
select列名
from 表名 as 別名1,表名 as 別名2;
select *
from 薪水錶 as s1,薪水錶 as s2;
需要加上什麼條件嗎?顯然觀察上述圖表,需滿足僱員編號(1)<=僱員編號,而題意當前員工的薪水需要滿足結束日期 = '9999-01-01',並按僱員編號升序排列:
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.僱員編號;
最後用 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.僱員編號;
【本題考點】
對於“累計”問題,要想到用聚合函式作為視窗函式。比如累計求和,用sum。
sum(列名) over (order by <用於排序的列名>)
累計求平均值,用avg。
avg(列名) over (order by <用於排序的列名>)
所以,我們可以得出“累計求和”問題的萬能模板是:
select 列1,列2,
sum(列名) over (partition by <用於分組的列名>
order by <用於排序的列名>) as 累計值的別名
from 表名;
【舉一反三】
下表為確診人數表,包含日期和該日期對應的新增確診人數
按照日期進行升序排列,查詢日期、確診人數以及對應的累計確診人數。
參考答案:
select 日期,確診人數,
sum(確診人數) over (order by 日期) as 累計確診人數
from 確診人數表;
查詢結果
推薦:如何從零學會sql?