我从 SCD-2 表中检索具有许多参数的数据,并且我需要仅使用其中一个来构建自己的 SCD-2。因此,我需要摆脱过多的间隔。请推荐一种算法以最佳方式执行该操作。
我从源表中收到的内容:
我需要将其转换为:
我从 SCD-2 表中检索具有许多参数的数据,并且我需要仅使用其中一个来构建自己的 SCD-2。因此,我需要摆脱过多的间隔。请推荐一种算法以最佳方式执行该操作。
我从源表中收到的内容:
我需要将其转换为:
这显然很复杂,因为相同的“值”可以在多个组中重复 - 所以你不能只使用简单的 MIN/MAX 函数。您可能可以在 javascript 存储过程中对此进行编码,但我想我会尝试在(几乎)纯 SQL 中找到解决方案。
挑战是在每次值更改时尝试创建一个“组” - 这样您就可以在组内的日期上执行简单的 MIN/MAX。我(希望!)解决这个问题的方式如下:
代码
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, 15, '2020-01-03', '2020-01-05'),
(19999923, 15, '2020-01-06', '2020-01-08'),
(19999923, 3434, '2020-01-09', '2020-01-12'),
(19999923, 3434, '2020-01-13', '2020-01-15'),
(19999923, 15, '2020-01-16', '2020-01-20'),
(19999923, 15, '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, VALUE1, row_actual_from, row_actual_to, CHK_MIN
,CASE WHEN CHK_MIN IS NULL THEN LAG(CHK_MIN,1,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, VALUE1, MIN(ROW_ACTUAL_FROM), MAX(ROW_ACTUAL_TO)
FROM T2
GROUP BY KEY1, VALUE1, 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, value1 integer, row_actual_from date, row_actual_to date);
INSERT INTO #source
VALUES
(19999923, 15, '2020-01-01', '2020-01-02'),
(19999923, 15, '2020-01-03', '2020-01-05'),
(19999923, 15, '2020-01-06', '2020-01-08'),
(19999923, 11, '2020-01-09', '2020-01-12'),
(19999923, 3434, '2020-01-13', '2020-01-15'),
(19999923, 11, '2020-01-16', '2020-01-20'),
(19999923, 15, '2020-01-21', '2020-02-02'),
(19999923, 3434, '2020-02-03', '2020-02-10'),
(19999923, 3434, '2020-02-11', '2020-02-19'),
(19999923, 3434, '2020-02-20', '2020-02-25'),
(19999923, 99, '2020-02-26', '9999-12-31');
第 1 步:确定单个价值期的开始和结束。
请注意,在 LAG/LEAD 中,本质上是将值作为 NULL 替换(例如 -99),它与列中的可能值不匹配。
DROP TABLE IF EXISTS #step1;
SELECT
key1, value1, row_actual_from, row_actual_to
, period_start = CASE WHEN LAG(value1, 1, -99) OVER (PARTITION BY key1 ORDER BY row_actual_from) <> value1 THEN 1 ELSE 0 END
, period_end = CASE WHEN LEAD(value1, 1, -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 的 row_actual_to 分配给开始。
如果一个值的周期只有一行,则该行的 period_start 和 period_end 设置为 1,因此总和为 2。在这种情况下,row_acutal_to 的内容已经具有想要的值。
DROP TABLE IF EXISTS #step2;
SELECT
key1, value1, row_actual_from, row_actual_to, 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, value1, valid_from, valid_to
FROM #step2
WHERE period_start = 1
ORDER BY key1, row_actual_from;