記錄我的一次基於dataworks大數據平臺的關於多欄位SUM求和的ODPS SQL優化

記錄我的一次基於dataworks大數據平臺的關於多欄位SUM求和的ODPS SQL優化

背景

在我所需要做的需求中,有這樣一個場景,簡化描述即是,基於阿里雲dataworks平臺ODPS SQL腳本統計出商品訂單表(order_info)的每種商品在每個月的銷量情況。
訂單表主要字段(訂單編號、商品編號、銷售月份,每筆訂單銷售多少件)如下,每個分區訂單表數據爲2.5億條
ORDER_INFO

需要統計呈現的report效果如下:
商品每月出件表

問題描述

前提:這個需求本身比這樣的描述要複雜的多,需要進行多重f(x)計算,因與此次優化關係不大,遂省去,且僅基於其中一個分區進行操作,簡化抽離以方便閱讀,但是以下時間結果皆是基於需求本身得到的,而非簡化後執行得到的。

一開始,我覺得這樣的需求很簡單,直接按商品進行group by分組,再用case判斷是哪個月份,最後對滿足條件的數據進行sum求和不就好啦?所以根據這樣的思路,我寫下了以下sql

SELECT  goods_id
        ,SUM(CASE WHEN sale_month=7 THEN num END ) AS month7
        ,SUM(CASE WHEN sale_month=8 THEN num END ) AS month8
        ,SUM(CASE WHEN sale_month=9 THEN num END ) AS month9
FROM    order_info
GROUP BY goods_id where goods_id='A'
;

爲了便於驗證,我在篩選條件上加上了商品條件爲A的條件限制(where goods_id=‘A’),針對我原本的需求,這樣的sql code 執行了約3分鐘左右,對此結果,我認爲是正常。於是我放開了goods_id='A’的條件限制,讓其直接執行所有數據

SELECT  goods_id
        ,SUM(CASE WHEN sale_month=7 THEN num END ) AS month7
        ,SUM(CASE WHEN sale_month=8 THEN num END ) AS month8
        ,SUM(CASE WHEN sale_month=9 THEN num END ) AS month9
FROM    order_info
GROUP BY goods_id
;

於是,悲劇的事情發生了,竟然跑了2個小時都沒有跑完,這不科學!
這裏我解釋一下,在原有需求裏,由於我有***數十甚至上百個***SUM求和函數,還有其他函數,加上數據本身也是多表關聯而來,並且數據量有上億之多,所以執行的非常慢,logview顯示經過2個小時才執行了60%。經過logview和explain分析,最終發現執行較慢的原因就在sum求和上。

解決方案

由於SUM函數較多,而SUM函數的求和模式又有規律可循,所以,最終解決方案定爲,將【每月賣出件數】作爲一個欄位字段來進行統一求和,再將這一列數據轉化爲行。

思路如下:
第一步,統計出每個商品每個月一共銷售多少件,作爲臨時表1
temp1

第二步,在臨時表1的基礎上,將銷售月份字段轉成行形式,存爲臨時表2,即
temp2
第三步,在臨時表2的基礎上進行按goods id分組統計,對每月銷量進行求和(此處根據我的業務場景,我使用的是求和函數,但不限於求和,可根據自身的業務場景,使用判空或者取MAX都可),最終即得到了我們想要的report
商品每月出件表

實行如下:
有了這樣的思路,我們就趕緊實行起來吧~話不多說,優化後的sql如下

SELECT  goods_id
        ,SUM(month7) AS month7 ---此處不限於sum函數
        ,SUM(month8) AS month8 ---此處不限於sum函數
        ,SUM(month9) AS month9 ---此處不限於sum函數
FROM    (
            SELECT  goods_id
                    ,(CASE WHEN sale_month=7 THEN MONTH_NUM END) AS month7
                    ,(CASE WHEN sale_month=8 THEN MONTH_NUM END) AS month8
                    ,(CASE WHEN sale_month=9 THEN MONTH_NUM END) AS month9
            FROM    (
                        SELECT  goods_id
                                ,sale_month
                                ,SUM(num) AS MONTH_NUM
                        FROM    order_info
                        GROUP BY goods_id
                                 ,sale_month
                    ) temp1
        ) temp2
GROUP BY goods_id
;

經過這樣的優化後,原需求sql執行時間縮短爲30分鐘。

小結

A 代碼並不是越短越好,以代碼精簡程度評判優劣的形式不可取;
B 如在大數據開發中遇到類似需要多欄位求和十分耗時,而求和欄位又有共性的問題,不妨嘗試將多欄位先轉化成列,再轉化爲行,即用 行列轉化 的思想解決來這樣的問題。
C 如有不足指出請指出,如有更好的解決辦法歡迎補充,謝謝~

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