类型 2 缓慢变化的维度转换 (Teradata 14.10)

Type 2 slowly changing dimensions conversion (Teradata 14.10)

我有一个table喜欢

KeyField DeltaField SomeField Row_Ins_Ts
1        a          1         '2016-01-01 00:00:00'
1        a          2         '2016-01-02 00:00:00'
1        b          3         '2016-01-03 00:00:00'         
1        a          4         '2016-01-04 00:00:00'
2        d          5         '2016-01-01 00:00:00'
2        d          6         '2016-01-02 00:00:00'
2        e          7         '2016-01-03 00:00:00'
2        e          8         '2016-01-04 00:00:00'

我需要获取给定 KeyField 的每个 DeltaField 值的时间间隔。

上述数据集的结果集为:

KeyField DeltaField Rec_Strt_Ts            Rec_End_Ts
1        a          '2016-01-01 00:00:00'  '2016-01-02 23:59:59'
1        b          '2016-01-03 00:00:00'  '2016-01-03 23:59:59'      
1        a          '2016-01-04 00:00:00'  '9999-12-31 23:59:59' 
2        d          '2016-01-01 00:00:00'  '2016-01-02 23:59:59'
2        e          '2016-01-03 00:00:00'  '9999-12-31 23:59:59' 

我正在尝试使用 Vertica 而不是 Teradata,但幸运的是两者都支持 ANSI 99 标准:Window 函数,也称为 OLAP 或分析函数,以及带有几个全局的 WITH 子句 table 表达式相互依赖。

第一个全局table表达式只是为了生成你的示例数据;第二个属于查询,因为您通常不能将 OLAP 函数放入 WHERE 子句中,因此需要在子查询中获取它。 LAG() OLAP 函数是您需要的,以便能够过滤掉与其前身具有相同 deltafield 的行。因此,在最终查询中,我可以过滤 deltafield <> prev_deltafield 并使用 LEAD() OLAP 函数并从中减去一秒以获得结束时间戳。我使用 IFNULL() 函数来满足我没有 LEAD() 或 LAG() 值的情况。 IFNULL() 的同义词可以是 NVL() 或 VALUE()。 COALESCE() 也可以,但速度较慢,因为它有可变数量的参数。看这里:

WITH foo(keyfield,deltafield,somefield,row_ins_ts) AS (
          SELECT 1,'a',1, TIMESTAMP '2016-01-01 00:00:00'
UNION ALL SELECT 1,'a',2, TIMESTAMP '2016-01-02 00:00:00'
UNION ALL SELECT 1,'b',3, TIMESTAMP '2016-01-03 00:00:00'
UNION ALL SELECT 1,'a',4, TIMESTAMP '2016-01-04 00:00:00'
UNION ALL SELECT 2,'d',5, TIMESTAMP '2016-01-01 00:00:00'
UNION ALL SELECT 2,'d',6, TIMESTAMP '2016-01-02 00:00:00'
UNION ALL SELECT 2,'e',7, TIMESTAMP '2016-01-03 00:00:00'
UNION ALL SELECT 2,'e',8, TIMESTAMP '2016-01-04 00:00:00'
)
,    add_previous_deltafield AS (
SELECT
  keyfield
, deltafield
, LAG(deltafield) OVER(PARTITION BY keyfield ORDER BY row_ins_ts) AS prev_deltafield
, row_ins_ts
FROM foo
)
SELECT
  keyfield
, deltafield
, row_ins_ts AS rec_start_ts
, IFNULL(
    LEAD(row_ins_ts) OVER(
      PARTITION BY keyfield ORDER BY row_ins_ts
    ) - INTERVAL '1 SECOND'
  , '9999-12-31 23:59:59'
  ) AS rec_end_ts
FROM add_previous_deltafield
WHERE deltafield <> IFNULL(prev_deltafield,'')
ORDER BY keyfield;

玩的开心 - 理智的马可

作为 Window 函数(很好,因为它们是可移植的)的替代方法,您可以使用 Teradata 的内置 Period 逻辑以及一些内置函数来快速解决这个问题。

基本上这将分为三个部分:

  1. 将您的时间戳转换为句点 (Timestamp)。 Teradata 中的期间类型具有起点和终点,采用 Period(<begindate>, <enddate>) 形式,并使用日期或时间戳。
  2. 使用内置函数TD_NORMALIZE_OVERLAP_MEET,我们可以根据一个或多个字段将重叠或会议期间的多个记录压缩在一起。
  3. 然后我们取该函数产生的周期的开始。

在你的例子中:

WITH subtbl(keyfield, deltafield, durations) AS
(
    SELECT
        keyfield,
        deltafield,
        PERIOD(row_ins_ts, row_ins_ts + INTERVAL '1' DAY )  AS durations
    FROM
        <yourtable>
) 
SELECT keyfield, deltafield, BEGIN(durations) 
FROM TABLE
    (
        TD_NORMALIZE_OVERLAP_MEET (NEW VARIANT_TYPE(subtbl.keyfield, subtbl.deltafield), subtbl.durations)
        RETURNS (keyfield INTEGER, deltafield CHAR(1), durations PERIOD(TIMESTAMP(0)), numRecords INTEGER) 
        HASH BY keyfield, deltafield
        LOCAL ORDER BY keyfield, deltafield, durations
    ) AS dt(keyfield, deltafield, durations, numRecords)    
ORDER BY 1, 2;

输出:

+----------+------------+------------------+
| keyfield | deltafield | BEGIN(durations) |
+----------+------------+------------------+
|        1 | a          | 1/4/2016 0:00    |
|        1 | a          | 1/1/2016 0:00    |
|        1 | b          | 1/3/2016 0:00    |
|        2 | d          | 1/1/2016 0:00    |
|        2 | e          | 1/3/2016 0:00    |
+----------+------------+------------------+