前言
作为一名数据人员,报表开发可能是难以绕过的一项工作。运营、市场、销售、财务等部门总会有各种各样的报表需求。我也算是经历过不少风雨的一号人物了,为财务开发利润报表仍然让我头疼不已。
设计思路
一、灵活的参数
利润报表中有很多参数并不是固化的,而是在不同的时期会有不同的值。比如一些费率的变化,为了实现灵活的参数配置,需要设计一张参数表,满足不同时期不同取值的需求。
最后采用以历史拉链表的形式存储key-value对来实现灵活的可变参数取用。
参数表长这样:
我们只需要将时间点落在start_time
和end_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()
即可。代码就不再亮了,又臭又长。
后话
数值计算完成后,测试核准数据着实花了不少时间。利润报表涉及的数据之多,参数之复杂在我经手的任务中也是数一数二的,希望以后不要有这种需求啦,看数据看地头疼,对数据也对地眼花。
手动扶额-。-