Я пытаюсь решить проблему, когда я хочу объединить перекрывающиеся интервалы для заданного идентификатора столбца, но я также хочу объединить их на основе иерархии/приоритета. У меня есть 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 строк. Обратите внимание, что для некоторых идентификаторов могут быть только некоторые значения приоритета или только одно, решение должно позаботиться об этом.
ожидаемый ввод и вывод:
Типичная проблема "пробелов и островов".
@GordonLinoff Большой запрос. добавил некоторые детали
@GordonLinoff Пожалуйста, не говорите, что мне нужно объединить их для данного идентификатора.
Я изменил свой ответ, чтобы принять это во внимание.
Это проблема «объединения» островов. Одно из решений состоит в том, чтобы найти, где начинаются острова, и вычислить кумулятивную сумму начал. Определить начало можно, увидев, где нет перекрытий:
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, работает отлично. Я собираюсь проверить это еще на нескольких примерах данных, чтобы проверить решение.
Конечно. имейте в виду - ответ основан на логике и примерных данных, которые вы представили в своем вопросе. поэтому, если вы попадете в случай, который не охвачен, пожалуйста, задайте новый вопрос. тем временем рассмотрите возможность голосования, и если подтвердится работа - вернитесь и примите :o)
Можете ли вы также поделиться решением, в котором мы объединяем интервалы на основе только идентификатора и без столбца приоритета. Я хочу посмотреть, как это работает по отношению к решению, которое я придумал. Я хочу сравнить время выполнения, поскольку данные, с которыми я имею дело, довольно велики. Спасибо.
добавил это к моему ответу
Привет, @Mikhail, во втором решении (слияние без учета приоритета) можно ли изменить решение, чтобы у нас был еще один приоритет столбца, который является максимальным значением приоритета в течение этого периода времени? поэтому у нас будет дополнительный столбец приоритета со значением 3 для строки 1 (длительность от 0 до 36) и значением 2 для строки 2 (длительность от 41 до 47).
@ParveshAngrula - если у вас есть новый вопрос - задайте новый вопрос, и мы будем рады вам помочь
опубликовал новый вопрос, как вы предложили. stackoverflow.com/questions/56498923/…
@ParveshAngrula - конечно. я проверю это в ближайшее время, если кто-то не ответит на него к тому времени: о)
Спасибо за вашу помощь.
Кстати. я ответил на этот вопрос
MySQL или BigQuery? Они совсем другие.