MS SQL Server:使用较少的变量将SCD-2转换为SCD-2

问题描述

我从具有许多参数的SCD-2表中检索数据,我只需要使用其中一个来构建自己的SCD-2。因此,我需要摆脱过多的时间间隔。请推荐一种算法,以最佳方式执行该操作。

我从源表中收到什么:

what I receive from the source table

我需要将其转换为:

I need to transform it to:

解决方法

显然,由于相同的“值”可以在多个组中重复的事实,这很复杂-因此您不能只使用简单的MIN / MAX函数。您可能可以在javascript存储过程中对此进行编码,但是我想我会尝试在(几乎)纯SQL中找到解决方案。

挑战在于,每次值更改时都尝试创建一个“组”-这样您就可以在组中的日期上执行简单的MIN / MAX。我(希望如此!)解决此问题的方法如下:

  1. 创建一个CTE,只要当前行的值与上一行的值不同,就将计算字段设置为序列中的下一个值;如果没有差异,则将该字段设置为null-这很重要,因为下一个CTE中的LAG函数处理NULL的方式
  2. 创建第二个CTE,将计算的分组列设置为上一列中创建的计算列中的最后一个非空值-使用LAG函数设置忽略空值
  3. 在第二CTE中,按键,值和分组列查询最小和最大日期值分组

代码

CREATE TABLE SRC_TABLE (key1 integer,value1 integer,row_actual_from date,row_actual_to date);

INSERT INTO SRC_TABLE
VALUES
(19999923,15,'2020-01-01','2020-01-02'),(19999923,'2020-01-03','2020-01-05'),'2020-01-06','2020-01-08'),3434,'2020-01-09','2020-01-12'),'2020-01-13','2020-01-15'),'2020-01-16','2020-01-20'),'2020-01-21','9999-12-31');


create or replace sequence seq_01 start = 1 increment = 1;
WITH T1 AS (
  SELECT KEY1,VALUE1,row_actual_from,row_actual_to,CASE WHEN LAG(VALUE1,1,0) OVER (PARTITION BY KEY1 ORDER BY row_actual_from ASC) = VALUE1 THEN null ELSE seq_01.nextval END AS CHK_MIN
  from SRC_TABLE
  order by row_actual_from
),T2 AS (
  SELECT KEY1,CHK_MIN,CASE WHEN CHK_MIN IS NULL THEN LAG(CHK_MIN,0) IGNORE NULLS OVER (PARTITION BY KEY1 ORDER BY row_actual_from ASC) ELSE CHK_MIN END AS CHK_MIN_GRP
  FROM T1
)
SELECT KEY1,MIN(ROW_ACTUAL_FROM),MAX(ROW_ACTUAL_TO)
FROM T2
GROUP BY KEY1,CHK_MIN_GRP
;

结果

KEY1        VALUE1      MIN(ROW_ACTUAL_FROM)    MAX(ROW_ACTUAL_TO)
19999923        15      2020-01-01              2020-01-08
19999923        3434    2020-01-09              2020-01-15
19999923        15      2020-01-16              9999-12-31
,

您可以使用以下步骤获得所需的结果。当然,您可以使用子选择或CTE一步完成全部操作,但是为了获得更好的可追溯性,我更喜欢临时表。

DROP TABLE IF EXISTS #source;
CREATE TABLE #source (key1 integer,row_actual_to date);
 
INSERT INTO #source
VALUES
(19999923,11,'2020-02-02'),'2020-02-03','2020-02-10'),'2020-02-11','2020-02-19'),'2020-02-20','2020-02-25'),99,'2020-02-26','9999-12-31');

第1步:确定单个值周期的开始和结束。

请注意,在LAG / LEAD中,必须有一个值作为NULL替换(例如-99),该值与列中的可能值不匹配。

    DROP TABLE IF EXISTS #step1;
    SELECT
        key1,value1,period_start = CASE WHEN LAG(value1,-99) OVER (PARTITION BY key1 ORDER BY row_actual_from) <> value1 THEN 1 ELSE 0 END,period_end   = CASE WHEN LEAD(value1,-99) OVER (PARTITION BY key1 ORDER BY row_actual_from) <> value1 THEN 1 ELSE 0 END
    INTO #step1
    FROM #source
    ORDER BY key1,row_actual_from;

第2步:对开始/结束行进行过滤,然后将end_rowual_to分配给开始的行。

如果值的周期只有一行,则该行的period_start和period_end设置为1,因此总和为2。在这种情况下,row_acutal_to的内容已经具有所需的值。

    DROP TABLE IF EXISTS #step2;
    SELECT
        key1,period_start,period_end,valid_from = row_actual_from,valid_to   = CASE (period_start + period_end)
                     WHEN 1 THEN LEAD(row_actual_to,1) OVER (PARTITION BY key1,value1 ORDER BY row_actual_from)
                     WHEN 2 THEN row_actual_to ELSE NULL END
    INTO #step2
    FROM #step1
    WHERE (period_start + period_end) > 0
    ORDER BY key1,row_actual_from;

第3步:过滤(调整后)价值周期的开始行。

    SELECT key1,valid_from,valid_to
    FROM   #step2
    WHERE  period_start = 1
    ORDER BY key1,row_actual_from;