1. 程式人生 > >記一次“噁心”的利潤報表開發(基於阿里雲MaxCompute)

記一次“噁心”的利潤報表開發(基於阿里雲MaxCompute)

前言

作為一名資料人員,報表開發可能是難以繞過的一項工作。運營、市場、銷售、財務等部門總會有各種各樣的報表需求。我也算是經歷過不少風雨的一號人物了,為財務開發利潤報表仍然讓我頭疼不已。

設計思路

一、靈活的引數

利潤報表中有很多引數並不是固化的,而是在不同的時期會有不同的值。比如一些費率的變化,為了實現靈活的引數配置,需要設計一張引數表,滿足不同時期不同取值的需求。
最後採用以歷史拉鍊表的形式儲存key-value對來實現靈活的可變引數取用。
引數表長這樣:
這裡寫圖片描述
我們只需要將時間點落在start_timeend_time之間,即該條記錄的存活週期之內,我們就可以回溯至任意歷史時刻的引數數值。

二、中間表處理

利潤報表的基礎資料涉及到授信、進件、放款、商品價格、保險等資料,這些資料並不是存放在一起的,而是存放於不同的資料庫中。當然我們可以邊計算邊連線表,然而基於運算效率與開發效率的考慮,我們直接建立一張彙總所有基礎資料的中間表。(本文出現的所有程式碼均已脫敏,請隨意食用)
中間表生成過程:

INSERT OVERWRITE TABLE sinafenqi_dw.dw_risk_with_draw
SELECT rwd.loan_id, phone, product_id, req_time, return_time
    , risk_channel, source, terms, account_id, pay_handle_time
    , money, type, user_type, market_price, supply_price
    , market_price - supply_price AS
diff_price, ins_amount, ins_time FROM ( SELECT loan_id, phone, product_id, req_time, return_time , risk_channel, source, GET_JSON_OBJECT(req_content, '$.content.withDrawReq.terms') AS terms FROM sinafenqi_ods.risk_with_draw WHERE pt = ${bdp.system.bizdate} AND result = '2'
) rwd LEFT OUTER JOIN ( SELECT lr_all.account_id, pay_handle_time, money, requisition_id, type , CASE WHEN pay_handle_time <= min_time THEN 'new' WHEN min_time IS NULL THEN 'new' ELSE 'old' END AS user_type FROM ( SELECT account_id, pay_handle_time, money, requisition_id, type FROM sinafenqi_ods.public_loan_requisition WHERE pt = ${bdp.system.bizdate} ) lr_all LEFT OUTER JOIN ( SELECT account_id, MIN(pay_handle_time) AS min_time FROM sinafenqi_ods.public_loan_requisition WHERE pt = ${bdp.system.bizdate} AND state = 'close' GROUP BY account_id ) lr_close ON lr_all.account_id = lr_close.account_id ) lr ON rwd.loan_id = lr.requisition_id LEFT OUTER JOIN ( SELECT requisition_id, market_price, supply_price FROM ( SELECT requisition_id, order_id FROM sinafenqi_ods.t_loan_bill WHERE pt = ${bdp.system.bizdate} AND status = 1 AND requisition_id IS NOT NULL ) a LEFT OUTER JOIN ( SELECT order_id, sku_id, market_price FROM sinafenqi_ods.t_order_item WHERE pt = ${bdp.system.bizdate} ) b ON a.order_id = b.order_id LEFT OUTER JOIN ( SELECT id, supply_price FROM sinafenqi_ods.t_item_sku WHERE pt = ${bdp.system.bizdate} AND status = 1 ) c ON b.sku_id = c.id ) pr ON rwd.loan_id = pr.requisition_id LEFT OUTER JOIN ( SELECT loan_id, round(amount / 100, 2) AS ins_amount , updated_at AS ins_time FROM ( SELECT id, loan_id, amount, updated_at FROM sinafenqi_ods.insurance_order WHERE pt = ${bdp.system.bizdate} ) io JOIN ( SELECT insurance_id FROM sinafenqi_ods.insurance_plan WHERE pt = ${bdp.system.bizdate} AND status = 'SUCCESS' AND stages = 1 ) ip ON io.id = ip.insurance_id ) ins ON rwd.loan_id = ins.loan_id

其實中間表處理也比較簡單,基本就是幾張表的關聯,當時為什麼要做中間表是還有個很重要的原因是需要新增新老戶的判斷。即利潤報表需要區分新戶和續貸戶,這個欄位在業務資料庫是不原生存在的。所以我們需要通過條件判斷處理出來。
邏輯為:
1. 找出客戶的第一筆成功結清的貸款時間;
2. 如果不存在成功結清的貸款單,則判斷為新戶;
3. 如果存在成功結清的貸款單,所有貸款時間小於等於此單的訂單均記為新戶單,所有貸款時間大於此單的訂單記為續貸戶單。
具體程式碼上面已經給出,可以自行查閱。

三、每日資料計算過程

原始碼:

INSERT OVERWRITE TABLE sinafenqi_bi.t_profit_weibo_goods_result PARTITION (pt=${bdp.system.bizdate})
SELECT data_date, user_type, terms
    , round(credit_apply_count_byterms, 0), entery_adopt_count
    , loan_count, loan_amount, round(loan_income, 2) AS loan_income
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round(loan_income * 100 / loan_amount, 2), '%')
    END AS loan_rate, market_price_sum, supply_price_sum, diff_price_sum
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round(diff_price_sum * 100 / loan_amount, 2), '%')
    END AS diff_rate, punish_fine, punish_rate, ins_count
    , round(ins_amount, 2) AS ins_amount
    , round(ins_income, 2) AS ins_income
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round(ins_income * 100 / loan_amount, 2), '%')
    END AS ins_rate
    , CASE 
        WHEN loan_count = 0 THEN NULL
        ELSE concat(round(ins_count * 100 / loan_count, 2), '%')
    END AS ins_penetrance
    , round(loan_income + diff_price_sum + punish_fine + ins_income, 2) AS total_income
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round((loan_income + diff_price_sum + punish_fine + ins_income) * 100 / loan_amount, 2), '%')
    END AS total_rate, customer_cost
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round(customer_cost * 100 / loan_amount, 2), '%')
    END AS customer_rate, round(capital_cost_amount, 2) AS capital_cost_amount
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round(capital_cost_amount * 100 / loan_amount, 2), '%')
    END AS capital_cost_rate, check_credit_amount
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round(check_credit_amount * 100 / loan_amount, 2), '%')
    END AS check_credit_rate, sign_cost_amount
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round(sign_cost_amount * 100 / loan_amount, 2), '%')
    END AS sign_cost_rate, round(credit_apply_count_byterms * FOUR_ELEM_COST, 2) AS four_elements_cost_amount
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round(credit_apply_count_byterms * FOUR_ELEM_COST * 100 / loan_amount, 2), '%')
    END AS four_elements_cost_rate, helppay_cost_amount
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round(helppay_cost_amount * 100 / loan_amount, 2), '%')
    END AS helppay_cost_rate, credit_tel_cost_amount
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round(credit_tel_cost_amount * 100 / loan_amount, 2), '%')
    END AS credit_tel_cost_rate, collection_cost_rate, collection_cost_amount, withhold_cost_amount
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round(withhold_cost_amount * 100 / loan_amount, 2), '%')
    END AS withhold_cost_rate, channel_cost_rate, round(channel_cost_amount, 2) AS channel_cost_amount
    , CASE 
        WHEN user_type = 'new' THEN round(credit_apply_count_byterms * PRODUCT_SMS_NUM * PRODUCT_SMS_COST, 2)
        WHEN user_type = 'old' THEN round(entery_adopt_count * PRODUCT_SMS_NUM_OLD * PRODUCT_SMS_COST, 2)
    END AS product_msg_amount
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round(credit_apply_count_byterms * PRODUCT_SMS_NUM * PRODUCT_SMS_COST * 100 / loan_amount, 2), '%')
    END AS product_msg_rate
    , round(customer_cost + capital_cost_amount + check_credit_amount + sign_cost_amount + credit_apply_count_byterms * FOUR_ELEM_COST + helppay_cost_amount + credit_tel_cost_amount + collection_cost_amount + withhold_cost_amount + channel_cost_amount + credit_apply_count_byterms * PRODUCT_SMS_NUM * PRODUCT_SMS_COST, 2) AS total_cost_amount
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round((customer_cost + capital_cost_amount + check_credit_amount + sign_cost_amount + credit_apply_count_byterms * FOUR_ELEM_COST + helppay_cost_amount + credit_tel_cost_amount + collection_cost_amount + withhold_cost_amount + channel_cost_amount + credit_apply_count_byterms * PRODUCT_SMS_NUM * PRODUCT_SMS_COST) * 100 / loan_amount, 2), '%')
    END AS product_msg_rate, bad_debt_amount
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round(bad_debt_amount * 100 / loan_amount, 2), '%')
    END AS bad_debt_rate
    , round(loan_income + diff_price_sum + punish_fine + ins_income - customer_cost - capital_cost_amount - check_credit_amount - sign_cost_amount - credit_apply_count_byterms * FOUR_ELEM_COST - helppay_cost_amount - credit_tel_cost_amount - collection_cost_amount - withhold_cost_amount - channel_cost_amount - credit_apply_count_byterms * PRODUCT_SMS_NUM * PRODUCT_SMS_COST - bad_debt_amount, 2) AS gross_profit_amount
    , CASE 
        WHEN loan_amount = 0 THEN NULL
        ELSE concat(round((loan_income + diff_price_sum + punish_fine + ins_income - customer_cost - capital_cost_amount - check_credit_amount - sign_cost_amount - credit_apply_count_byterms * FOUR_ELEM_COST - helppay_cost_amount - credit_tel_cost_amount - collection_cost_amount - withhold_cost_amount - channel_cost_amount - credit_apply_count_byterms * PRODUCT_SMS_NUM * PRODUCT_SMS_COST - bad_debt_amount) * 100 / loan_amount, 2), '%')
    END AS gross_profit_rate
FROM (
    SELECT t.data_date, user_type, terms
        , CASE 
            WHEN user_type = 'old' THEN NULL
            WHEN total_entry_adopt_count = 0 THEN 0
            ELSE credit_apply_count * entery_adopt_count / total_entry_adopt_count
        END AS credit_apply_count_byterms, entery_adopt_count, loan_count, loan_amount
        , CASE terms
            WHEN 3 THEN loan_amount * (1 - BAD_P3_BAD_DEBT) * YEAR_P3 * 3
            WHEN 6 THEN loan_amount * (1 - BAD_P6_BAD_DEBT) * YEAR_P6 * 6
            WHEN 9 THEN loan_amount * (1 - BAD_P9_BAD_DEBT) * YEAR_P9 * 9
            WHEN 12 THEN loan_amount * (1 - BAD_P12_BAD_DEBT) * YEAR_P12 * 12
            WHEN 18 THEN loan_amount * (1 - BAD_P18_BAD_DEBT) * YEAR_P18 * 18
            WHEN 24 THEN loan_amount * (1 - BAD_P24_BAD_DEBT) * YEAR_P24 * 24
        END AS loan_income, market_price_sum, supply_price_sum, diff_price_sum, OVER_INTEREST_INCOME_RATIO AS punish_rate
        , loan_amount * OVER_INTEREST_INCOME_RATIO AS punish_fine, ins_count, ins_amount
        , ins_amount * INSURANCE_INCOME_RATIO / (1 + INSURANCE_INTEREST) AS ins_income
        , loan_amount * GUEST_STAGE_AMT_RATIO + diff_price_sum * GUEST_PRODUCT_DIFFAMT_RATIO AS customer_cost
        , CASE terms
            WHEN 3 THEN FUND_FIXED_PARAM / FUND_USAGE_RATE / 12 * terms * loan_amount * FUND_3P_RATIO
            WHEN 6 THEN FUND_FIXED_PARAM / FUND_USAGE_RATE / 12 * terms * loan_amount * FUND_6P_RATIO
            WHEN 9 THEN FUND_FIXED_PARAM / FUND_USAGE_RATE / 12 * terms * loan_amount * FUND_9P_RATIO
            WHEN 12 THEN FUND_FIXED_PARAM / FUND_USAGE_RATE / 12 * terms * loan_amount * FUND_12P_RATIO
            WHEN 18 THEN FUND_FIXED_PARAM / FUND_USAGE_RATE / 12 * terms * loan_amount * FUND_18P_RATIO
            WHEN 24 THEN FUND_FIXED_PARAM / FUND_USAGE_RATE / 12 * terms * loan_amount * FUND_24P_RATIO
        END AS capital_cost_amount
        , CASE user_type
            WHEN 'new' THEN entery_adopt_count * CREDIT_DRAW_ADUIT_PASS
            WHEN 'old' THEN entery_adopt_count * CREDIT_DRAW_ADUIT_PASS_OLD
        END AS check_credit_amount, loan_count * ELEC_SIGN_COST AS sign_cost_amount, loan_count * PAID_COST AS helppay_cost_amount
        , CASE 
            WHEN user_type = 'old' THEN NULL
            WHEN total_entry_adopt_count = 0 THEN 0
            ELSE (AUDIT_FIRST_PEOPLE_NUM * AUDIT_FIRST_UNIT_COST + AUDIT_TEL_PEOPLE_NUM * AUDIT_TEL_UNIT_COST) / 30 * AUDIT_PEOPLE_ALLOT_RATIO * entery_adopt_count / total_entry_adopt_count
        END AS credit_tel_cost_amount, COLLECT_COST AS collection_cost_rate, loan_amount * COLLECT_COST AS collection_cost_amount
        , loan_count * terms * WITHHOLD_COST AS withhold_cost_amount, CHANNEL_COST AS channel_cost_rate
        , loan_amount * CHANNEL_COST AS channel_cost_amount
        , CASE terms
            WHEN 3 THEN loan_amount * BAD_P3_BAD_DEBT
            WHEN 6 THEN loan_amount * BAD_P6_BAD_DEBT
            WHEN 9 THEN loan_amount * BAD_P9_BAD_DEBT
            WHEN 12 THEN loan_amount * BAD_P12_BAD_DEBT
            WHEN 18 THEN loan_amount * BAD_P18_BAD_DEBT
            WHEN 24 THEN loan_amount * BAD_P24_BAD_DEBT
        END AS bad_debt_amount, FOUR_ELEM_COST, PRODUCT_SMS_NUM, PRODUCT_SMS_COST, PRODUCT_SMS_NUM_OLD
        , CREDIT_DRAW_ADUIT_PASS_OLD
    FROM (
        SELECT ${bdp.system.bizdate} AS data_date, user_type, terms, SUM(CASE 
                WHEN to_char(from_unixtime(return_time / 1000), 'yyyymmdd') = ${bdp.system.bizdate} THEN 1
                ELSE 0
            END) AS entery_adopt_count -- 進件通過筆數
            , SUM(CASE 
                WHEN to_char(from_unixtime(pay_handle_time / 1000), 'yyyymmdd') = ${bdp.system.bizdate} THEN 1
                ELSE 0
            END) AS loan_count -- 分期筆數
            , SUM(CASE 
                WHEN to_char(from_unixtime(pay_handle_time / 1000), 'yyyymmdd') = ${bdp.system.bizdate} THEN money
                ELSE 0
            END) AS loan_amount -- 分期金額
            , SUM(CASE 
                WHEN to_char(from_unixtime(pay_handle_time / 1000), 'yyyymmdd') = ${bdp.system.bizdate} THEN market_price
                ELSE 0
            END) AS market_price_sum -- 訂單價
            , SUM(CASE 
                WHEN to_char(from_unixtime(pay_handle_time / 1000), 'yyyymmdd') = ${bdp.system.bizdate} THEN supply_price
                ELSE 0
            END) AS supply_price_sum -- 採購價
            , SUM(CASE 
                WHEN to_char(from_unixtime(pay_handle_time / 1000), 'yyyymmdd') = ${bdp.system.bizdate} THEN diff_price
                ELSE 0
            END) AS diff_price_sum -- 差價收入
            , SUM(CASE 
                WHEN to_char(ins_time, 'yyyymmdd') = ${bdp.system.bizdate} THEN 1
                ELSE 0
            END) AS ins_count -- 保險筆數
            , SUM(CASE 
                WHEN to_char(ins_time, 'yyyymmdd') = ${bdp.system.bizdate} THEN ins_amount
                ELSE 0
            END) AS ins_amount -- 扣保金額
        FROM sinafenqi_dw.dw_risk_with_draw
        WHERE source = 'storeloanweibo'
            AND type = 'GOODS'
        GROUP BY user_type, 
            terms
    ) t
    JOIN (
        SELECT ${bdp.system.bizdate} AS data_date, MAX(CASE 
                WHEN key_name = 'YEAR_P24' THEN value
            END) AS YEAR_P24, MAX(CASE 
                WHEN key_name = 'YEAR_P18' THEN value
            END) AS YEAR_P18
            , MAX(CASE 
                WHEN key_name = 'YEAR_P12' THEN value
            END) AS YEAR_P12, MAX(CASE 
                WHEN key_name = 'YEAR_P9' THEN value
            END) AS YEAR_P9
            , MAX(CASE 
                WHEN key_name = 'YEAR_P6' THEN value
            END) AS YEAR_P6, MAX(CASE 
                WHEN key_name = 'YEAR_P3' THEN value
            END) AS YEAR_P3
            , MAX(CASE 
                WHEN key_name = 'OVER_INTEREST_INCOME_RATIO' THEN value
            END) AS OVER_INTEREST_INCOME_RATIO, MAX(CASE 
                WHEN key_name = 'INSURANCE_INTEREST' THEN value
            END) AS INSURANCE_INTEREST
            , MAX(CASE 
                WHEN key_name = 'INSURANCE_INCOME_RATIO' THEN value
            END) AS INSURANCE_INCOME_RATIO, MAX(CASE 
                WHEN key_name = 'GUEST_STAGE_AMT_RATIO' THEN value
            END) AS GUEST_STAGE_AMT_RATIO
            , MAX(CASE 
                WHEN key_name = 'GUEST_PRODUCT_DIFFAMT_RATIO' THEN value
            END) AS GUEST_PRODUCT_DIFFAMT_RATIO, MAX(CASE 
                WHEN key_name = 'FUND_FIXED_PARAM' THEN value
            END) AS FUND_FIXED_PARAM
            , MAX(CASE 
                WHEN key_name = 'FUND_USAGE_RATE' THEN value
            END) AS FUND_USAGE_RATE, MAX(CASE 
                WHEN key_name = 'FUND_24P_RATIO' THEN value
            END) AS FUND_24P_RATIO
            , MAX(CASE 
                WHEN key_name = 'FUND_18P_RATIO' THEN value
            END) AS FUND_18P_RATIO, MAX(CASE 
                WHEN key_name = 'FUND_12P_RATIO' THEN value
            END) AS FUND_12P_RATIO
            , MAX(CASE 
                WHEN key_name = 'FUND_9P_RATIO' THEN value
            END) AS FUND_9P_RATIO, MAX(CASE 
                WHEN key_name = 'FUND_6P_RATIO' THEN value
            END) AS FUND_6P_RATIO
            , MAX(CASE 
                WHEN key_name = 'FUND_3P_RATIO' THEN value
            END) AS FUND_3P_RATIO, MAX(CASE 
                WHEN key_name = 'CREDIT_DRAW_ADUIT_PASS' THEN value
            END) AS CREDIT_DRAW_ADUIT_PASS
            , MAX(CASE 
                WHEN key_name = 'ELEC_SIGN_COST' THEN value
            END) AS ELEC_SIGN_COST, MAX(CASE 
                WHEN key_name = 'PAID_COST' THEN value
            END) AS PAID_COST
            , MAX(CASE 
                WHEN key_name = 'AUDIT_FIRST_UNIT_COST' THEN value
            END) AS AUDIT_FIRST_UNIT_COST, MAX(CASE 
                WHEN key_name = 'AUDIT_TEL_UNIT_COST' THEN value
            END) AS AUDIT_TEL_UNIT_COST
            , MAX(CASE 
                WHEN key_name = 'AUDIT_FIRST_PEOPLE_NUM' THEN value
            END) AS AUDIT_FIRST_PEOPLE_NUM, MAX(CASE 
                WHEN key_name = 'AUDIT_TEL_PEOPLE_NUM' THEN value
            END) AS AUDIT_TEL_PEOPLE_NUM
            , MAX(CASE 
                WHEN key_name = 'AUDIT_PEOPLE_ALLOT_RATIO' THEN value
            END) AS AUDIT_PEOPLE_ALLOT_RATIO, MAX(CASE 
                WHEN key_name = 'COLLECT_COST' THEN value
            END) AS COLLECT_COST
            , MAX(CASE 
                WHEN key_name = 'WITHHOLD_COST' THEN value
            END) AS WITHHOLD_COST, MAX(CASE 
                WHEN key_name = 'CHANNEL_COST' THEN value
            END) AS CHANNEL_COST
            , MAX(CASE 
                WHEN key_name = 'BAD_P24_BAD_DEBT' THEN value
            END) AS BAD_P24_BAD_DEBT, MAX(CASE 
                WHEN key_name = 'BAD_P18_BAD_DEBT' THEN value
            END) AS BAD_P18_BAD_DEBT
            , MAX(CASE 
                WHEN key_name = 'BAD_P12_BAD_DEBT' THEN value
            END) AS BAD_P12_BAD_DEBT, MAX(CASE 
                WHEN key_name = 'BAD_P9_BAD_DEBT' THEN value
            END) AS BAD_P9_BAD_DEBT
            , MAX(CASE 
                WHEN key_name = 'BAD_P6_BAD_DEBT' THEN value
            END) AS BAD_P6_BAD_DEBT, MAX(CASE 
                WHEN key_name = 'BAD_P3_BAD_DEBT' THEN value
            END) AS BAD_P3_BAD_DEBT
            , MAX(CASE 
                WHEN key_name = 'FOUR_ELEM_COST' THEN value
            END) AS FOUR_ELEM_COST, MAX(CASE 
                WHEN key_name = 'PRODUCT_SMS_NUM' THEN value
            END) AS PRODUCT_SMS_NUM
            , MAX(CASE 
                WHEN key_name = 'PRODUCT_SMS_COST' THEN value
            END) AS PRODUCT_SMS_COST, MAX(CASE 
                WHEN key_name = 'PRODUCT_SMS_NUM_OLD' THEN value
            END) AS PRODUCT_SMS_NUM_OLD
            , MAX(CASE 
                WHEN key_name = 'CREDIT_DRAW_ADUIT_PASS_OLD' THEN value
            END) AS CREDIT_DRAW_ADUIT_PASS_OLD
        FROM sinafenqi_ods.t_profit_config
        WHERE source = 3
            AND is_delete = 0
            AND replace(start_time, '-', '') <= ${bdp.system.bizdate}
            AND replace(end_time, '-', '') >= ${bdp.system.bizdate}
    ) cs
    ON t.data_date = cs.data_date
    JOIN (
        SELECT data_date, credit_apply_count, entry_adopt_count AS total_entry_adopt_count_old -- 因為與歷史資料對不上對不上,棄用
        FROM sinafenqi_bi.t_weibo_daily_forweibo
        WHERE pt = ${bdp.system.bizdate}
    ) wb
    ON t.data_date = wb.data_date
    JOIN (
        SELECT ${bdp.system.bizdate} AS data_date, COUNT(*) AS total_entry_adopt_count
        FROM sinafenqi_dw.dw_risk_with_draw
        WHERE source = 'storeloanweibo'
            AND type = 'GOODS'
            AND user_type = 'new'
            AND to_char(from_unixtime(return_time / 1000), 'yyyymmdd') = ${bdp.system.bizdate}
    ) t2
    ON t.data_date = t2.data_date
) tt

程式碼解析:
1. 引數表是以key-value對的形式儲存的,為了能跟我們的中間表連線,需要將豎向排列的引數做成橫向排列。MAX()只是為了提取出這個值而已,實際上MIN()也是完全可以的。
2. 從中間表中統計出基礎資料後,與拿到的引數進行各種運算就是我們要的結果啦。因為許多計算存在層級關係,需要先算出某些值後才能計算後續的值,所以整個HQL嵌套了好幾層子查詢。其實最後幾個總收入、總成本相關的計算還可以繼續往上巢狀一層,但是由於層級已經夠多了,程式碼複雜度高,也同時為了省事直接各種加加減減解決了。實際上是再加一層子查詢比較科學,結構上會更加清晰明瞭,也便於後續的調整優化。

四、小計與月資料彙總

上面的只是計算的每日的按分期數統計的利潤資料,還需要一個小計,以及每月資料的統計。這個相對而言就比較簡單啦,直接對上面的結果聚合然後SUM()即可。程式碼就不再亮了,又臭又長。

後話

數值計算完成後,測試核准資料著實花了不少時間。利潤報表涉及的資料之多,引數之複雜在我經手的任務中也是數一數二的,希望以後不要有這種需求啦,看資料看地頭疼,對資料也對地眼花。
手動扶額-。-