记一次“恶心”的利润报表开发(基于阿里云MaxCompute)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhchs2012/article/details/80931013

前言

作为一名数据人员,报表开发可能是难以绕过的一项工作。运营、市场、销售、财务等部门总会有各种各样的报表需求。我也算是经历过不少风雨的一号人物了,为财务开发利润报表仍然让我头疼不已。

设计思路

一、灵活的参数

利润报表中有很多参数并不是固化的,而是在不同的时期会有不同的值。比如一些费率的变化,为了实现灵活的参数配置,需要设计一张参数表,满足不同时期不同取值的需求。
最后采用以历史拉链表的形式存储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()即可。代码就不再亮了,又臭又长。

后话

数值计算完成后,测试核准数据着实花了不少时间。利润报表涉及的数据之多,参数之复杂在我经手的任务中也是数一数二的,希望以后不要有这种需求啦,看数据看地头疼,对数据也对地眼花。
手动扶额-。-

猜你喜欢

转载自blog.csdn.net/zhchs2012/article/details/80931013