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

FastCode 63 8

За период получить количество дней, в которые проходило хотя бы одно мероприятие

Еще одна задача на интервалы (Gaps and Islands Problem). Имеется множество интервалов времени "занятости" помещений, оборудования, других ресурсов. Интервалы могут произвольным образом перекрываться. Требуется определить общее время занятости ресурсов.

Проблема здесь именно в необходимости учета пересечения интервалов, из-за чего сначала требуется определить начало и конец каждой занятости, а затем соединить их для определения длин.

Оказывается, что воспользовавшись идеями статьи "Расчет средних по периодам в запросе - это элементарно!", можно исключить необходимость второго соединения, поскольку суммарная занятость может быть определена как сумма расстояний до правого края "островов" минус сумма расстояний до левого края островов без необходимости определения длины каждого острова.

Если исходные интервалы заданы в таблице "Дано" с колонками "ДатаНачала" и "ДатаОкончания", то общее время занятости ресурсов в днях можно получить следующим достаточно простым запросом:

ВЫБРАТЬ
    СУММА(ВложенныйЗапрос.Разность) КАК ЗанятыхДней
ИЗ
    (ВЫБРАТЬ РАЗЛИЧНЫЕ
        1 - РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1, 1, 1), Дано.ДатаНачала, ДЕНЬ) КАК Разность
    ИЗ
        Дано КАК Дано
            ЛЕВОЕ СОЕДИНЕНИЕ Дано КАК Левее
            ПО (Левее.ДатаНачала < Дано.ДатаНачала)
                И Дано.ДатаНачала <= Левее.ДатаОкончания
    ГДЕ
        Левее.ДатаНачала ЕСТЬ NULL

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ РАЗЛИЧНЫЕ
        РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1, 1, 1), Дано.ДатаОкончания, ДЕНЬ)
    ИЗ
        Дано КАК Дано
            ЛЕВОЕ СОЕДИНЕНИЕ Дано КАК Правее
            ПО (Правее.ДатаНачала <= Дано.ДатаОкончания)
                И Дано.ДатаОкончания < Правее.ДатаОкончания
    ГДЕ
        Правее.ДатаОкончания ЕСТЬ NULL) КАК ВложенныйЗапрос

Во вложенном запросе здесь определяются левые и правые края островов соответственно. Точнее, расстояние до этих краев от начала времен. Расстояние для левого края сразу берется с минусом.

Если занятость требуется измерять не в днях, а в часах, минутах, секундах и тому подобное, достаточно изменить параметр в функции "РазностьДат".

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

Автор: ildarovich

OneTracker - трекер учета рабочего времени программиста 1С

Похожие публикации

Определение пересечения интервалов в кольце

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

Посчитать запросом суммарную длительность различных состояний

Разбиение произвольного периода на интервалы в запросе

Определить элементарные интервалы, образующиеся при пересечении всех исходных интервалов

Модератору