从列的子集计算最小记录版本集
Calculate minimum set of record versions from a subset of columns
我正在尝试破解一个看起来很容易解释的 SQL 问题:
- 我有一个 table,其中包含给定实体的多个版本(如 SCD 类型 2 维度)- 下面的 table
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');
我基本上想结束:
-- 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'),
意思是,我想要列 level1
的最小版本集。请注意,第 3 行和第 4 行将重复,但在这种情况下,我们将获得 min(start_ts)
和 max(end_ts)
来计算版本。
这是我尝试过的方法,但我惨遭失败...
-- 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。
这是一个缺口和孤岛问题。在这种情况下,我会使用 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);
请注意,这假设开始和结束时间戳中没有间隔。
这是如何工作的并不是很明显。我一般建议你只盯着子查询的结果看。通常很明显,两个行号之间的差异标识了您要聚合的组。
我正在尝试破解一个看起来很容易解释的 SQL 问题:
- 我有一个 table,其中包含给定实体的多个版本(如 SCD 类型 2 维度)- 下面的 table
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');
我基本上想结束:
-- 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'),
意思是,我想要列
level1
的最小版本集。请注意,第 3 行和第 4 行将重复,但在这种情况下,我们将获得min(start_ts)
和max(end_ts)
来计算版本。这是我尝试过的方法,但我惨遭失败...
-- 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。
这是一个缺口和孤岛问题。在这种情况下,我会使用 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);
请注意,这假设开始和结束时间戳中没有间隔。
这是如何工作的并不是很明显。我一般建议你只盯着子查询的结果看。通常很明显,两个行号之间的差异标识了您要聚合的组。