Рассчитать минимальный набор версий записей из подмножества столбцов - PullRequest
0 голосов
/ 05 декабря 2018

Я пытаюсь взломать проблему 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');

В основном я хочу получить:

-- 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.

1 Ответ

0 голосов
/ 05 декабря 2018

Это проблема пробелов и островков.В этом случае я бы использовал 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);

Обратите внимание, что это предполагает отсутствие пробелов в начальной и конечной отметках времени.

Как это работает не сразуочевидно.Я вообще рекомендую просто посмотреть на результаты подзапроса.Как правило, довольно очевидно, что разница между двумя номерами строк определяет группы, которые вы хотите объединить.

...