从列的子集计算记录版本的最小集合

时间:2018-12-05 19:22:08

标签: sql snowflake-datawarehouse

我正在尝试破解一个看起来很简单的SQL问题:

  1. 我有一个表,其中包含给定实体的多个版本(例如SCD类型2维)-下面的表temp.test

内容:

DROP TABLE IF EXISTS temp.test;
CREATE TEMP TABLE temp.test (
  row_id   INTEGER IDENTITY (1, 1),
  id       VARCHAR,
  start_ts TIMESTAMP,
  end_ts   TIMESTAMP,
  level1   VARCHAR,
  level2   VARCHAR
);

INSERT INTO temp.test (id, start_ts, end_ts, level1, level2) VALUES
  ('a', '1970-01-01 00:00:00.000000', '2017-12-31 23:59:59.999999', 'ABC1', 'ABC2'),
  ('a', '2018-01-01 00:00:00.000000', '2018-12-31 23:59:59.999999', 'DEF1', 'DEF2'),
  ('a', '2019-01-01 00:00:00.000000', '2019-12-31 23:59:59.999999', 'ABC1', 'GHI2'),
  ('a', '2020-01-01 00:00:00.000000', '2020-12-31 23:59:59.999999', 'ABC1', 'JKL2');
  1. 我基本上想结束:

    -- Desired output
    ('a', '1970-01-01 00:00:00.000000', '2017-12-31 23:59:59.999999', 'ABC1'),
    ('a', '2018-01-01 00:00:00.000000', '2018-12-31 23:59:59.999999', 'DEF1'),
    ('a', '2019-01-01 00:00:00.000000', '2020-12-31 23:59:59.999999', 'ABC1'),
    
  2. 意味着,我想要列level1的最低版本集合。请注意,第三行和第四行将重复,但是在这种情况下,我们将获得min(start_ts)max(end_ts)来计算版本。

  3. 这是我尝试过的方法,但我失败了...

    -- Wrong
    SELECT
      id,
      min(start_ts) AS start_ts,
      max(end_ts)   AS end_ts,
      level1
    FROM temp.test
    GROUP BY id, level1
    ORDER BY 2;
    
    -- Wrong
    SELECT DISTINCT
      id,
      FIRST_VALUE(start_ts) OVER(PARTITION BY id, level1 ORDER BY start_ts) AS start_ts,
      LAST_VALUE(end_ts) OVER(PARTITION BY id, level1 ORDER BY start_ts)    AS end_ts,
      level1
    FROM temp.test
    ORDER BY 2;
    

必须有某种神奇的方式来获得我需要的输出。您有什么建议?

注意:我正在使用Snowflake,但这只是标准的SQL。

1 个答案:

答案 0 :(得分:2)

这是一个孤岛问题。在这种情况下,我将使用row_number()方法:

SELECT id, level1,
       MIN(start_ts) as start_ts, MAX(end_ts) as end_ts
FROM (SELECT t.*,
             ROW_NUMBER() OVER (PARTITION BY id ORDER BY start_ts) as seqnum_i,
             ROW_NUMBER() OVER (PARTITION BY id, level1 ORDER BY start_ts) as seqnum_il,
      FROM temp.test t
     ) t
GROUP BY id, level1, (seqnum_i - seqnum_il);

请注意,这假设开始和结束时间戳记之间没有间隙。

这是如何工作的,目前尚不清楚。我通常建议您只盯着子查询的结果。通常很明显,两个行号之间的差异标识了要聚合的组。