Объединение перекрывающихся временных интервалов на основе иерархии в SQL

Я пытаюсь решить проблему, когда я хочу объединить перекрывающиеся интервалы для заданного идентификатора столбца, но я также хочу объединить их на основе иерархии/приоритета. У меня есть start_time и stop_time для каждого интервала, и каждый интервал имеет связанную с ним иерархию/приоритет.

Это следующие столбцы таблицы:

id, start_time, stop_time, priority

Мне удалось решить проблему, в которой я не учитывал приоритет, но я борюсь с этим.

Red colour: p1 (priority 1)
Blue Colour: p2 (priority 2)
Green colour: p3 (priority 3)

Обратите внимание, что в примере ниже у нас будет 9 строк с одинаковым идентификатором, а на выходе будет 6 строк. Обратите внимание, что для некоторых идентификаторов могут быть только некоторые значения приоритета или только одно, решение должно позаботиться об этом.

ожидаемый ввод и вывод:

Объединение перекрывающихся временных интервалов на основе иерархии в SQL

MySQL или BigQuery? Они совсем другие.

Gordon Linoff 30.05.2019 23:19

Типичная проблема "пробелов и островов".

The Impaler 30.05.2019 23:20

@GordonLinoff Большой запрос. добавил некоторые детали

Tars 30.05.2019 23:24

@GordonLinoff Пожалуйста, не говорите, что мне нужно объединить их для данного идентификатора.

Tars 30.05.2019 23:26

Я изменил свой ответ, чтобы принять это во внимание.

Gordon Linoff 31.05.2019 00:33
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
5
280
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это проблема «объединения» островов. Одно из решений состоит в том, чтобы найти, где начинаются острова, и вычислить кумулятивную сумму начал. Определить начало можно, увидев, где нет перекрытий:

select id, priority, min(start_time), max(stop_time)
from (select t.*,
             countif (coalesce(prev_stop_time, stop_time) < stop_time) over (partition by id, priority order by start_time) as grp
      from (select t.*,
                   max(stop_time) over (partition by id, priority order by start_time rows between unbounded preceding and 1 preceding) as prev_stop_time
            from t
           ) t
      ) t
group by id, priority, grp;
Ответ принят как подходящий

Ниже приведен стандартный SQL BigQuery.

#standardSQL
WITH check_times AS (
  SELECT id, start_time AS time FROM `project.dataset.table` UNION DISTINCT
  SELECT id, stop_time AS time FROM `project.dataset.table` 
), distinct_intervals AS (
  SELECT id, time AS start_time, LEAD(time) OVER(PARTITION BY id ORDER BY time) stop_time
  FROM check_times
), deduped_intervals AS (
  SELECT a.id, a.start_time, a.stop_time, MIN(priority) priority
  FROM distinct_intervals a
  JOIN `project.dataset.table` b
  ON a.id = b.id 
  AND a.start_time BETWEEN b.start_time AND b.stop_time 
  AND a.stop_time BETWEEN b.start_time AND b.stop_time
  GROUP BY a.id, a.start_time, a.stop_time
), combined_intervals AS (
  SELECT id, MIN(start_time) start_time, MAX(stop_time) stop_time, ANY_VALUE(priority) priority 
  FROM (
    SELECT id, start_time, stop_time, priority, COUNTIF(flag) OVER(PARTITION BY id ORDER BY start_time) grp
    FROM (
      SELECT id, start_time, stop_time, priority, 
        start_time != IFNULL(LAG(stop_time) OVER(PARTITION BY id ORDER BY start_time), start_time) OR
        priority != IFNULL(LAG(priority) OVER(PARTITION BY id ORDER BY start_time), -1) flag
      FROM deduped_intervals
    )
  )
  GROUP BY id, grp
)
SELECT *
FROM combined_intervals
-- ORDER BY id, start_time

Если обратиться к выборочным данным из вашего вопроса - результат

Can you also share a solution where we merge intervals based on just id and no priority column

Я просто немного изменил запрос выше, чтобы игнорировать приоритет

#standardSQL
WITH check_times AS (
  SELECT id, start_time AS TIME FROM `project.dataset.table` UNION DISTINCT
  SELECT id, stop_time AS TIME FROM `project.dataset.table` 
), distinct_intervals AS (
  SELECT id, TIME AS start_time, LEAD(TIME) OVER(PARTITION BY id ORDER BY TIME) stop_time
  FROM check_times
), deduped_intervals AS (
  SELECT a.id, a.start_time, a.stop_time 
  FROM distinct_intervals a
  JOIN `project.dataset.table` b
  ON a.id = b.id 
  AND a.start_time BETWEEN b.start_time AND b.stop_time 
  AND a.stop_time BETWEEN b.start_time AND b.stop_time
  GROUP BY a.id, a.start_time, a.stop_time
), combined_intervals AS (
  SELECT id, MIN(start_time) start_time, MAX(stop_time) stop_time 
  FROM (
    SELECT id, start_time, stop_time, COUNTIF(flag) OVER(PARTITION BY id ORDER BY start_time) grp
    FROM (
      SELECT id, start_time, stop_time, 
        start_time != IFNULL(LAG(stop_time) OVER(PARTITION BY id ORDER BY start_time), start_time) flag
      FROM deduped_intervals
    )
  )
  GROUP BY id, grp
)
SELECT *
FROM combined_intervals
-- ORDER BY id, start_time   

с результатом

Row id  start_time  stop_time    
1   1   0           36   
2   1   41          47   

Большое спасибо @mikhail, работает отлично. Я собираюсь проверить это еще на нескольких примерах данных, чтобы проверить решение.

Tars 31.05.2019 01:40

Конечно. имейте в виду - ответ основан на логике и примерных данных, которые вы представили в своем вопросе. поэтому, если вы попадете в случай, который не охвачен, пожалуйста, задайте новый вопрос. тем временем рассмотрите возможность голосования, и если подтвердится работа - вернитесь и примите :o)

Mikhail Berlyant 31.05.2019 01:42

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

Tars 31.05.2019 01:42

добавил это к моему ответу

Mikhail Berlyant 31.05.2019 01:50

Привет, @Mikhail, во втором решении (слияние без учета приоритета) можно ли изменить решение, чтобы у нас был еще один приоритет столбца, который является максимальным значением приоритета в течение этого периода времени? поэтому у нас будет дополнительный столбец приоритета со значением 3 для строки 1 (длительность от 0 до 36) и значением 2 для строки 2 (длительность от 41 до 47).

Tars 06.06.2019 23:32

@ParveshAngrula - если у вас есть новый вопрос - задайте новый вопрос, и мы будем рады вам помочь

Mikhail Berlyant 06.06.2019 23:35

опубликовал новый вопрос, как вы предложили. stackoverflow.com/questions/56498923/…

Tars 07.06.2019 19:34

@ParveshAngrula - конечно. я проверю это в ближайшее время, если кто-то не ответит на него к тому времени: о)

Mikhail Berlyant 07.06.2019 19:35

Спасибо за вашу помощь.

Tars 07.06.2019 19:36

Кстати. я ответил на этот вопрос

Mikhail Berlyant 07.06.2019 22:55

Другие вопросы по теме