hive/spark-sql經典筆試面試題(一)累加計算

問題描述

在數據倉庫中有這樣一張表,記錄了每個月的營業額,數據如下:
表名:test:
字段:1、month ;2、money
營業額數據
需要統計截止到每個月份的營業總額,如:
截止到2019年1月份營業總額爲10元
截止到2019年2月份營業總額爲10+10=20元
截止到2019年3月份營業總額爲10+10+10=30元
以此類推…
先貼出最終結果的樣子:
在這裏插入圖片描述

方案 一 簡單粗暴計算

直接使用where條件過濾符合的數據,使用sum函數進行計算

select sum(money) from test where month='201901'
union all
select sum(money) from test where month<='201902'
union all 
select sum(money) from test where month<='201903'
……

優點:這是最容易想到的解決方法,實現起來又方便
缺點:此方法雖能解決問題,可略顯笨重,且同一張表掃描次數過多,執行速度緩慢,不可取。

方案二 巧用case when

無論是mysql還是hive 或者是spark-sql都支持case when,這是一個強大的功能,
配合上sum,count,等聚合函數,可以計算出結果。

select
    sum(case when month=201901 then money else null end) as sum_money_01,
    sum(case when month<=201902 then money else null end) as sum_money_02,
    sum(case when month<=201903 then money else null end) as sum_money_03,
    sum(case when month<=201904 then money else null end) as sum_money_04,
    sum(case when month<=201905 then money else null end) as sum_money_05,
    sum(case when month<=201906 then money else null end) as sum_money_06,
    sum(case when month<=201907 then money else null end) as sum_money_07,
    sum(case when month<=201908 then money else null end) as sum_money_08,
    sum(case when month<=201909 then money else null end) as sum_money_09,
    sum(case when month<=201910 then money else null end) as sum_money_10,
    sum(case when month<=201911 then money else null end) as sum_money_11,
    sum(case when month<=201912 then money else null end) as sum_money_12
from 
    test

優點:對test表只進行了一次掃描,判斷month是屬於哪個範圍,符合寫定範圍則進行聚合運算,否則置爲null利用sum函數不計算null值的特性,得到我們想要的答案。從某種程度上來說,case when 在此處發揮的是where的功能。
缺點:寫法還是有些複雜,這是12個月我們就需要寫12個語句,若是需求更加的細化,比如計算一年中截止到每一天的營業額,我們就需要寫365個語句,顯然是不可能的,可拓展性不高

方案三 使用自連接實現

第一步:將test表自己與自己進行連接,不過條件不是等於,而是大於等於,先列出這樣的結果
(爲了便於閱讀加上了order by 實際上不需要排序)
在這裏插入圖片描述

我們看到離想要的結果已經很接近了,只要按照t0.month進行分組,對t1.money進行聚合,就可以得到想要的答案,
第二步,最終代碼

select 
    t0.month,
    sum(t1.money)
from
    (select month,money from test)t0
join
    (select month,money from test)t1
on t0.month>=t1.month
group by t0.month

優點:此種寫法拓展性比較高,不用關心計算粒度多大,都能實現需求
缺點:實際上此處進行的是一種不完全的笛卡爾積,增加了數據量,若數據量比較大的情況下可能會導致執行速度下降。
簡單計算一下擴大的倍數,例子中是6條,最終結果需要計算的是1+2+3+4+5+6=21條
若原來是n條數據,最終結果就是1+2+3+…+n=(1+n)*n/2,擴大倍數爲(1+n)/2倍

方案四 使用窗口函數

select 
    month,
    sum(money) over(order by month rows between unbounded preceding and current row) as sum_money
from 
    test

執行結果:
窗口函數執行結果

over從句中:
1、可使用partition by語句,使用一個或者多個原始數據類型的列
2、可使用partition by與order by語句,使用一個或者多個數據類型的分區或者排序列

關鍵是理解rows between含義,也叫做window子句:
preceding:往前
following:往後
current row:當前行
unbounded:無界限(起點或終點)
unbounded preceding:表示從前面的起點
unbounded following:表示到後面的終點

當order by後面缺少窗口從句條件,窗口規範默認是
row between unbounded preceding and current row. (從起點到當前行)
當order by和窗口從句都缺失, 窗口規範默認是
row between unbounded preceding and unbounded following.
(從起點到終點)

此處沒有使用partition by語句進行分組,計算的是所有數據,
當計算到201903月份的時候,會從前面的起點(unbounded preceding)累加到當前行( current row)也就是10+10+10=30,這樣也就得出了我們想要的結果

優點:寫法簡單,執行效率最高。而且需求變更,或者數據變化的接受能力都很強
缺點:比較難以理解,需要掌握窗口函數的使用。

關於窗口函數還有更多的用法、更強大的功能,更多詳細介紹請移步大佬的博客:

李國冬——Hive常用函數大全(二)(窗口函數、分析函數、增強group)

拓展:

  • 如果給的數據是比較細化的數據,比如給定的是每天的數據,需要按月彙總,那就先按照每個月的營業額進行彙總,得到開頭那樣的數據。
  • 如果不是一家的營業額而是多家,比如a商店,b商店,c商店,那就在此基礎上進行分組group by

關於累加計算的介紹本篇寫了幾種解法,可根據需求進行自主選擇。
希望大家積極交流共同進步。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章