You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

多表联查行数异常排查及获取min(TIME_DAY_KEY)的SQL咨询

关联多表后行数暴增的原因及解决方案

嘿,先别急着怪那个月份转换语句——它其实没惹祸!咱们来拆解下问题根源,以及怎么在保持4000行的前提下拿到最早消费日期:

为什么行数会暴增?

核心问题出在postpaid_summary表的关联方式和GROUP BY的字段,和月份处理语句完全无关:

  • 你用了JOIN postpaid_summary p(内连接),而postpaid_summary里同一个MSISDN(对应ACTIVATIONS的act_phone_no)大概率有多条消费记录(每个TIME_DAY_KEY对应一条)。这就导致前面三张表的每一行,都会和这个号码对应的所有消费记录做笛卡尔积,直接把行数撑起来了。
  • 更关键的是你的GROUP BY里还包含了p.TIME_DAY_KEY——这意味着每个不同的TIME_DAY_KEY都会单独成组,完全达不到你想要的“按主表行取最早消费日期”的目的,反而会把所有匹配的消费记录都保留下来,自然行数就爆炸到10万+了。

怎么解决?保持4000行同时获取min(TIME_DAY_KEY)

要先对postpaid_summary预聚合,提前按MSISDN算出最早的TIME_DAY_KEY,再把这个聚合后的结果和前面的表关联,这样就不会产生行膨胀了。

修改后的SQL如下:

SELECT 
    a.act_actdevice, 
    a.act_phone_no, 
    a.bi_account_id, 
    a.packag_start_date, 
    TO_CHAR(TRUNC(a.packag_start_date, 'MONTH'), 'MON-YYYY') AS PACKAG_START_DATE_MONTHYEAR, 
    a.retailer_name, 
    a.retailer_type, 
    a.dms_id as "DSR/BPR_ID", 
    a.dsr_name as "DSR/BPR_NAME", 
    a.agent_type, 
    a.distributor_id, 
    a.distributor_name, 
    a.SALES_DISTRICT, 
    a.profileid, 
    s.district, 
    s.province, 
    c.identification_number, 
    c.account_type, 
    c.account_status, 
    c.activation_date, 
    c.permanent_disconnection_date, 
    c.temporary_disconnection_date, 
    c.status_change_date, 
    c.credit_limit, 
    c.average_monthly_bill_amount, 
    c.primary_packag_start__date, 
    c.package_code, 
    c.sales_channel, 
    c.site_id, 
    c.district_name, 
    c.usage_arpu, 
    c.bill_to_contact_name, 
    p.first_consumption_date 
FROM ACTIVATIONS a 
LEFT JOIN customer c ON TO_CHAR(a.act_phone_no) = c.msisdn_voice 
LEFT JOIN agent_dtl s ON a.dms_id = s.agent_id 
-- 关键改动:预聚合postpaid_summary,每个号码只取最早消费日期
LEFT JOIN (
    SELECT MSISDN, MIN(TIME_DAY_KEY) AS first_consumption_date
    FROM postpaid_summary
    GROUP BY MSISDN
) p ON a.act_phone_no = p.MSISDN 
WHERE a.packag_start_date BETWEEN TO_DATE('2020-01-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS') 
                              AND TO_DATE('2020-05-31 23:59:59', 'YYYY-MM-DD HH24:MI:SS') 
-- 注意:GROUP BY不再包含p的字段,因为已经提前聚合完成
GROUP BY 
    a.act_actdevice, 
    a.act_phone_no, 
    a.bi_account_id, 
    a.packag_start_date, 
    TO_CHAR(TRUNC(a.packag_start_date, 'MONTH'), 'MON-YYYY'), 
    a.retailer_name, 
    a.retailer_type, 
    a.dms_id, 
    a.dsr_name, 
    a.agent_type, 
    a.distributor_id, 
    a.distributor_name, 
    a.SALES_DISTRICT, 
    a.profileid, 
    s.district, 
    s.province, 
    c.identification_number, 
    c.account_type, 
    c.account_status, 
    c.activation_date, 
    c.permanent_disconnection_date, 
    c.temporary_disconnection_date, 
    c.status_change_date, 
    c.credit_limit, 
    c.average_monthly_bill_amount, 
    c.primary_packag_start__date, 
    c.package_code, 
    c.sales_channel, 
    c.site_id, 
    c.district_name, 
    c.usage_arpu, 
    c.bill_to_contact_name

几个关键说明:

  • 这里用了LEFT JOIN替代原来的JOIN,避免因为某些号码在postpaid_summary里没有消费记录而被过滤掉(如果你确实只想保留有消费记录的号码,改回JOIN即可)。
  • 预聚合子查询确保每个MSISDN只返回一行最早的消费日期,关联后不会增加主表的行数,就能保持原来的4000行左右。
  • 原来的月份转换语句TO_CHAR(TRUNC(a.packag_start_date, 'MONTH'), 'MON-YYYY')完全没问题,它只是把日期转成了月份格式,不会导致行数变化。

内容的提问来源于stack exchange,提问作者Shulaz Shan

火山引擎 最新活动