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

基于单日期列生成日期范围:忽略指定列构建SCD Type2

如何将MITMAS表转换为忽略指定列变动的SCD Type2维度表?

需要将表MITMAS转换为SCD Type2维度表,要求按MMITNO分组,仅追踪MMGRP2、MMGRP3、MMSTAT这三列的变化,忽略MMTST和MMASDF列的变动。

现有表结构及数据

CREATE TABLE tmp.MITMAS (
  MMITNO INT,
  MMSTAT INT,
  MMGRP2 VARCHAR(1),
  MMGRP3 VARCHAR(1),
  MMTST VARCHAR(2),
  MMASDF VARCHAR(2),
  ETL_Date DATE
);

INSERT INTO tmp.mitmas (MMITNO, MMSTAT, MMGRP2, MMGRP3, MMTST, MMASDF, ETL_Date)
VALUES 
(333556, 5, 'X', 'A', 'T1', 'H1', '2023-02-01'),
(333556, 5, 'X', 'A', 'T2', 'H1', '2023-02-02'),
(333556, 5, 'X', 'A', 'T1', 'H1', '2023-02-03'),
(333556, 5, 'Y', 'A', 'T2', 'H2', '2023-02-04'),
(333556, 6, 'Y', 'A', 'T1', 'H2', '2023-02-05'),
(333556, 6, 'Y', 'A', 'T1', 'H2', '2023-02-06'),
(333556, 6, 'Y', 'A', 'T1', 'H2', '2023-02-07'),
(324261, 5, 'X', 'A', 'T1', 'H1', '2023-02-01'),
(324261, 5, 'Y', 'A', 'T1', 'H2', '2023-02-02'),
(324261, 5, 'Y', 'A', 'T2', 'H2', '2023-02-03'),
(324261, 5, 'Y', 'A', 'T2', 'H1', '2023-02-04'),
(324261, 5, 'Y', 'B', 'T2', 'H2', '2023-02-05'),
(324261, 5, 'X', 'B', 'T1', 'H2', '2023-02-06'),
(324261, 5, 'X', 'B', 'T2', 'H1', '2023-02-07'),
(324261, 6, 'X', 'B', 'T2', 'H2', '2023-02-08'),
(324261, 6, 'X', 'B', 'T2', 'H2', '2023-02-09');

预期输出的SCD Type2表结构及数据

CREATE TABLE tmp.MITMAS_SCD (
  MMITNO INT NOT NULL,
  MMSTAT INT NOT NULL,
  MMGRP2 VARCHAR(50) NOT NULL,
  MMGRP3 VARCHAR(50) NOT NULL,
  StartDate DATE NOT NULL,
  EndDate DATE NOT NULL
);
INSERT INTO tmp.MITMAS_SCD (MMITNO, MMSTAT, MMGRP2, MMGRP3, StartDate, EndDate)
VALUES 
(333556, 5, 'X', 'A', '2023-02-01', '2023-02-04'),
(333556, 5, 'Y', 'A', '2023-02-04', '2023-02-05'),
(333556, 6, 'Y', 'A', '2023-02-05', '9999-12-31'),
(324261, 5, 'X', 'A', '2023-02-01', '2023-02-02'),
(324261, 5, 'Y', 'A', '2023-02-02', '2023-02-05'),
(324261, 5, 'Y', 'B', '2023-02-05', '2023-02-06'),
(324261, 5, 'X', 'B', '2023-02-06', '9999-12-31');

尝试的SQL及问题

当前尝试的SQL会把MMTST和MMASDF的变动也当作需要追踪的变更,生成过多不必要的时间区间:

SELECT
  MM.MMITNO,
  MM.MMSTAT,
  MM.MMGRP2,
  MM.MMGRP3,
  MM.ETL_Date AS StartDate,
  COALESCE(LEAD(MM.ETL_Date) OVER (PARTITION BY MM.MMITNO ORDER BY MM.ETL_Date), '9999-12-31') AS EndDate
FROM tmp.MITMAS MM;

解决方案

可以通过两种方式实现需求,核心是先过滤掉关键列无变化的重复行,再计算时间区间:

方法一:分组去重后计算区间

先按MMITNO、MMSTAT、MMGRP2、MMGRP3分组,取每个版本的最早日期作为开始日期,再通过LEAD获取下一个版本的开始日期作为当前版本的结束日期:

WITH deduplicated_data AS (
    SELECT 
        MMITNO,
        MMSTAT,
        MMGRP2,
        MMGRP3,
        MIN(ETL_Date) AS StartDate
    FROM tmp.MITMAS
    GROUP BY MMITNO, MMSTAT, MMGRP2, MMGRP3
    ORDER BY MMITNO, StartDate
),
ranked_data AS (
    SELECT 
        *,
        LEAD(StartDate) OVER (PARTITION BY MMITNO ORDER BY StartDate) AS NextStartDate
    FROM deduplicated_data
)
SELECT 
    MMITNO,
    MMSTAT,
    MMGRP2,
    MMGRP3,
    StartDate,
    COALESCE(NextStartDate, '9999-12-31') AS EndDate
FROM ranked_data;

方法二:标记变更行后过滤

通过LAG函数对比前后行的关键列组合,只保留首次出现或发生变更的行,再计算时间区间:

WITH flagged_changes AS (
    SELECT 
        MMITNO,
        MMSTAT,
        MMGRP2,
        MMGRP3,
        ETL_Date,
        -- 标记当前行与上一行关键列是否不同
        CASE 
            WHEN LAG(CONCAT(MMSTAT, MMGRP2, MMGRP3)) OVER (PARTITION BY MMITNO ORDER BY ETL_Date) 
                 != CONCAT(MMSTAT, MMGRP2, MMGRP3)
            THEN 1
            ELSE 0
        END AS is_change,
        -- 标记分组内的第一行
        ROW_NUMBER() OVER (PARTITION BY MMITNO ORDER BY ETL_Date) AS rn
    FROM tmp.MITMAS
),
filtered_data AS (
    SELECT 
        MMITNO,
        MMSTAT,
        MMGRP2,
        MMGRP3,
        ETL_Date AS StartDate
    FROM flagged_changes
    WHERE is_change = 1 OR rn = 1
)
SELECT 
    MMITNO,
    MMSTAT,
    MMGRP2,
    MMGRP3,
    StartDate,
    COALESCE(LEAD(StartDate) OVER (PARTITION BY MMITNO ORDER BY StartDate), '9999-12-31') AS EndDate
FROM filtered_data;

两种方法都能得到符合预期的SCD Type2结果,忽略MMTST和MMASDF的无关变动。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 13:37:02