При разработке систем планирования рабочего времени, графиков сменности или систем учета фактической работы часто возникает задача анализа пересечений. Представим ситуацию: несколько сотрудников работают в разное время, их смены накладываются друг на друга, и нам необходимо получить «чистые» временные интервалы с указанием списка всех людей, работающих в каждый конкретный момент. В этой статье мы подробно разберем, как решить эту задачу с помощью механизмов платформы 1С:Предприятие 8.
Допустим, у нас есть исходная таблица, где для каждого Сотрудник указаны НачСмены и КонСмены. Наша цель — разбить весь рабочий день на минимальные (элементарные) отрезки времени, внутри которых состав работающих сотрудников не меняется. Проблема стандартного соединения «каждый с каждым» заключается в том, что оно порождает огромное количество дублей и некорректных наложений. Мы воспользуемся проверенным алгоритмом, состоящим из трех последовательных этапов.
Для начала нам нужно собрать все «границы» времени — все моменты начала и все моменты окончания смен всех сотрудников. Эти точки станут границами наших будущих элементарных интервалов. Разберем, как это сделать в запросе. Сначала мы помещаем исходную таблицу значений во временную таблицу ВТ_ТабЗнач, а затем объединяем колонки начала и окончания в один список.
ВЫБРАТЬ
ТЗ.НачСмены КАК Период
ПОМЕСТИТЬ ВТ_ВсеТочки
ИЗ
ВТ_ТабЗнач КАК ТЗ
ОБЪЕДИНИТЬ
ВЫБРАТЬ
ТЗ.КонСмены
ИЗ
ВТ_ТабЗнач КАК ТЗ
;
Важно помнить, что использование оператора ОБЪЕДИНИТЬ (без слова ВСЕ) автоматически выполняет группировку и оставляет только уникальные значения времени. Таким образом, мы получаем упорядоченную «шкалу» всех событий дня.
Теперь, когда у нас есть все точки на временной оси, нам нужно превратить их в отрезки. Отрезок — это пара «Текущая точка» и «Следующая за ней точка». Чтобы реализовать это в языке запросов 1С, мы применим левое соединение таблицы ВТ_ВсеТочки саму с собой по условию «время начала меньше времени окончания».
Проанализируем этот запрос подробнее:
ВЫБРАТЬ
ВТ_Начала.Период КАК ПериодНач,
МИНИМУМ(ВТ_Концы.Период) КАК ПериодКон
ПОМЕСТИТЬ ВТ_ЭлементарныеИнтервалы
ИЗ
ВТ_ВсеТочки КАК ВТ_Начала
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ВсеТочки КАК ВТ_Концы
ПО ВТ_Начала.Период < ВТ_Концы.Период
ГРУППИРОВАТЬ ПО
ВТ_Начала.Период
;
В данном фрагменте кода мы для каждой временной метки ищем минимальную метку, которая больше текущей. Это гарантирует, что между ПериодНач и ПериодКон не попадет ни одна другая точка смены. Мы получили набор непересекающихся «кирпичиков» времени, из которых состоит рабочий день.
На заключительном этапе нам нужно понять, кто из сотрудников работал внутри каждого найденного элементарного интервала. Для этого мы соединяем наши интервалы с исходной таблицей ВТ_ТабЗнач по условию пересечения периодов.
Рассмотрим условие соединения. Чтобы избежать проблем с «лишними секундами», о которых часто упоминают разработчики, используем строгую проверку вхождения интервала. Если элементарный интервал [ПериодНач, ПериодКон] полностью лежит внутри смены сотрудника [НачСмены, КонСмены], значит, этот сотрудник в это время работал.
ВЫБРАТЬ
Интервалы.ПериодНач КАК НачИнтервала,
Интервалы.ПериодКон КАК КонИнтервала,
ТЗ.Сотрудник КАК Сотрудник
ИЗ
ВТ_ЭлементарныеИнтервалы КАК Интервалы
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ТабЗнач КАК ТЗ
ПО ТЗ.НачСмены <= Интервалы.ПериодНач
И ТЗ.КонСмены >= Интервалы.ПериодКон
УПОРЯДОЧИТЬ ПО
НачИнтервала
;
Важный момент: использование условий <= и >= здесь корректно, так как мы проверяем попадание целого интервала внутрь другого. Поскольку границы интервалов были сформированы из всех возможных точек начала и конца, ситуация, когда смена сотрудника начинается или заканчивается внутри элементарного интервала, физически невозможна.
После выполнения запроса мы получим таблицу, где один и тот же временной интервал может повторяться несколько раз (по количеству сотрудников). Чтобы привести это к виду «Один интервал — Список сотрудников через запятую», идеально подходит Система компоновки данных (СКД).
Разберем шаги настройки отчета:
Сотрудник в колонке «Выражение» напишем функцию: СоединитьСтроки(Сотрудник, ", ").НачИнтервала и КонИнтервала.Благодаря функции СоединитьСтроки(), платформа сама соберет всех сотрудников в одну ячейку, обеспечив наглядность отчета.
Проанализируем еще один способ, который часто применяется в высоконагруженных системах планирования (WFM). Он называется «Метод вех» или метод накопительного итога. Выясним причину его популярности: он позволяет за один проход по данным определить не только пересечения, но и количество одновременно работающих людей (нагрузку).
Алгоритм метода:
+1.-1.Этот метод особенно полезен, если вам не нужно выводить фамилии, а требуется только построить график нагрузки на подразделение.
При реализации запроса в реальной конфигурации 1С (например, ЗУП или ERP) мы можем столкнуться с незаполненными датами окончания смены. Если в системе допустимо, что сотрудник заступил на смену, но еще не закончил ее, запрос может «потерять» такие интервалы. Выясним, как этого избежать.
Рекомендуется при помещении данных во временную таблицу использовать функцию ЕСТЬNULL или проверку на пустую дату. В случае пустой даты окончания, подставляйте максимально возможное значение в системе ДАТАВРЕМЯ(3999, 12, 31, 23, 59, 59). Это позволит алгоритму формирования интервалов корректно обработать «открытые» смены.
Рассмотренная методика формирования элементарных интервалов является универсальной и подходит для решения большинства задач, связанных с анализом пересекающихся периодов времени в 1С.