You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于冷却期逻辑计算客户存续期的SQL问题及优化需求

客户存续期(Customer Tenure)计算的SQL解决方案

业务规则

  • 以满足客户身份连续前提下的最早账户开立日期作为存续期起点
  • 同一客户可同时拥有多个活跃账户
  • 6个月冷却期规则:客户保持连续身份需满足任一条件:
    • 某账户关闭后6个月内开立新账户
    • 该账户关闭前已有其他活跃账户在运行
  • 若账户关闭后超过6个月才开新账户,新账户的开立日期为新的存续期起点

场景示例(加粗为正确存续起始日期)

  • Customer_ID=11111:Account1开立日期为起点,因关闭账户前已有其他活跃账户
  • Customer_ID=11112:Account1开立日期为起点,因账户关闭后6个月内开立新账户
  • Customer_ID=11113:Account2开立日期为起点,因账户关闭后超6个月才开新账户
  • Customer_ID=11114:**Account1开立日期(2000-01-20)**为起点,因Account1关闭后6个月内开立Account3,但原查询错误将Account3开立日期作为起点

原查询问题

原查询仅按账户开立日期顺序处理,未考虑跨账户的重叠活跃或非顺序关联场景,导致在某些复杂账户关系下误判存续起点。

修正后的SQL方案

核心思路是通过**会话分组(Sessionization)**识别客户的连续活跃周期:将满足冷却期规则的账户归为同一会话,每个会话的最早开立日期即为该周期的存续起点,最终取客户当前所属会话的最早起点。

WITH account_events AS (
    -- 标准化账户结束日期:未关闭账户用当前日期替代
    SELECT 
        Customer_ID,
        ACCT_SERIAL_NUM,
        ACCT_STRT_DT,
        NVL(ACCT_END_DT, SYSDATE) AS ACCT_END_DT,
        -- 判断当前账户是否属于连续活跃周期
        CASE 
            -- 场景1:当前账户活跃期间存在其他并行活跃账户
            WHEN EXISTS (
                SELECT 1 
                FROM calc_customer_tenure ct2
                WHERE ct2.Customer_ID = ct.Customer_ID
                AND ct2.ACCT_SERIAL_NUM != ct.ACCT_SERIAL_NUM
                AND ct2.ACCT_STRT_DT <= ct.ACCT_END_DT
                AND NVL(ct2.ACCT_END_DT, SYSDATE) >= ct.ACCT_STRT_DT
            ) THEN 'Y'
            -- 场景2:当前账户关闭后6个月内有新账户开立
            WHEN NVL(LEAD(ACCT_STRT_DT) OVER(PARTITION BY Customer_ID ORDER BY ACCT_STRT_DT), SYSDATE) 
                 <= ADD_MONTHS(NVL(ACCT_END_DT, SYSDATE), 6) THEN 'Y'
            -- 场景3:当前账户是后续账户的前置活跃账户(后续账户活跃期间包含当前账户关闭时间)
            WHEN EXISTS (
                SELECT 1 
                FROM calc_customer_tenure ct2
                WHERE ct2.Customer_ID = ct.Customer_ID
                AND ct2.ACCT_SERIAL_NUM != ct.ACCT_SERIAL_NUM
                AND ct2.ACCT_STRT_DT > ct.ACCT_STRT_DT
                AND ct.ACCT_END_DT >= ct2.ACCT_STRT_DT
                AND ct.ACCT_END_DT <= NVL(ct2.ACCT_END_DT, SYSDATE)
            ) THEN 'Y'
            ELSE 'N'
        END AS IS_CONTINUOUS
    FROM calc_customer_tenure ct
),
session_groups AS (
    -- 为每个客户划分连续活跃会话:遇到非连续标记则开启新会话
    SELECT 
        *,
        SUM(CASE 
                WHEN ROW_NUMBER() OVER(PARTITION BY Customer_ID ORDER BY ACCT_STRT_DT) = 1 THEN 0
                WHEN IS_CONTINUOUS = 'N' THEN 1
                ELSE 0
            END) 
        OVER(PARTITION BY Customer_ID ORDER BY ACCT_STRT_DT ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS SESSION_ID
    FROM account_events
),
session_start_dates AS (
    -- 计算每个会话的最早开立日期
    SELECT 
        Customer_ID,
        SESSION_ID,
        MIN(ACCT_STRT_DT) AS SESSION_START_DATE
    FROM session_groups
    GROUP BY Customer_ID, SESSION_ID
),
current_session AS (
    -- 确定客户当前所属的会话:优先选择包含未关闭账户的最新会话
    SELECT 
        s.Customer_ID,
        s.SESSION_START_DATE,
        ROW_NUMBER() OVER(PARTITION BY s.Customer_ID 
                          ORDER BY s.SESSION_ID DESC, 
                                   CASE WHEN ae.ACCT_END_DT = SYSDATE THEN 0 ELSE 1 END) AS RN
    FROM session_start_dates s
    JOIN session_groups ae ON s.Customer_ID = ae.Customer_ID 
                          AND s.SESSION_ID = ae.SESSION_ID
)
-- 最终输出每个客户的存续期起点
SELECT 
    Customer_ID,
    SESSION_START_DATE AS CUST_TNUR
FROM current_session
WHERE RN = 1
ORDER BY Customer_ID;

方案优势

  • 覆盖所有核心业务场景:账户重叠活跃、冷却期内开新账户、非顺序账户关联
  • 会话分组逻辑清晰,避免因账户顺序问题导致的起点误判
  • 兼容未关闭账户的处理,用当前日期统一标准化结束日期

内容的提问来源于stack exchange,提问作者Himanshu Ginkal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 10:35:19