При составлении графиков работы, планировании расписаний смен или контроле занятости оборудования часто возникает задача проверки наложения временных интервалов. При попытке соединить таблицу интервалов саму с собой разработчики регулярно сталкиваются с двумя сложностями: некорректным определением самого факта пересечения через оператор МЕЖДУ и получением задвоенных «зеркальных» пар (например, Смена 1 пересекается со Сменой 2, и одновременно Смена 2 пересекается со Сменой 1). Разберем по шагам, как правильно составить запрос для поиска пересечений и исключить появление дублирующих строк.
Проанализируем распространенную ошибку: использование проверки вхождения границ через условие МЕЖДУ (или BETWEEN):
И (ТаблицаИнтервалов.ВремяНачалаИнтервала МЕЖДУ ТаблицаИнтервалов1.ВремяНачалаИнтервала И ТаблицаИнтервалов1.ВремяОкончанияИнтервала
ИЛИ ТаблицаИнтервалов.ВремяОкончанияИнтервала МЕЖДУ ТаблицаИнтервалов1.ВремяНачалаИнтервала И ТаблицаИнтервалов1.ВремяОкончанияИнтервала)
Такое условие не сработает, если один интервал полностью охватывает другой (например, смена А длится с 08:00 до 20:00, а смена Б — с 10:00 до 12:00). В этом случае ни начало, ни окончание смены А не лежат внутри диапазона 10:00–12:00, и запрос пропустит наложение.
Математически корректное условие: два интервала пересекаются тогда и только тогда, когда начало первого меньше окончания второго, а начало второго меньше окончания первого. Для полуоткрытых интервалов условие соединения выглядит следующим образом:
ПО Интервал1.ВремяНачала < Интервал2.ВремяОкончания
И Интервал2.ВремяНачала < Интервал1.ВремяОкончания
Если смежные границы (например, стык в стык: окончание в 10:00 и начало следующего в 10:00) не считаются наложением, необходимо использовать строгое неравенство < и >.
Когда мы соединяем таблицу саму с собой с условием НЕ Таблица1.Смена = Таблица2.Смена, платформа находит пару (Смена 1, Смена 2) и симметричную ей пару (Смена 2, Смена 1). Чтобы избавиться от перевернутых дублей без ресурсоемких группировок, зададим строгое отношение порядка между строками.
Разберем готовый текст запроса, где во временную таблицу помещаются интервалы смен и выполняется поиск уникальных пар пересечений:
ВЫБРАТЬ
СменыПериодыСмены.Ссылка КАК Смена,
СменыПериодыСмены.НомерСтроки КАК НомерСтрокиИнтервала,
СменыПериодыСмены.ВремяНачала КАК ВремяНачалаИнтервала,
СменыПериодыСмены.ВремяОкончания КАК ВремяОкончанияИнтервала
ПОМЕСТИТЬ ВТ_Интервалы
ИЗ
Справочник.Смены.ПериодыСмены КАК СменыПериодыСмены
ГДЕ
СменыПериодыСмены.Ссылка В(&СписокСмен)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Т1.Смена КАК Смена1,
Т1.НомерСтрокиИнтервала КАК НомерСтроки1,
Т1.ВремяНачалаИнтервала КАК ВремяНачала1,
Т1.ВремяОкончанияИнтервала КАК ВремяОкончания1,
Т2.Смена КАК Смена2,
Т2.НомерСтрокиИнтервала КАК НомерСтроки2,
Т2.ВремяНачалаИнтервала КАК ВремяНачала2,
Т2.ВремяОкончанияИнтервала КАК ВремяОкончания2,
// Вычисляем фактические границы пересечения
ВЫБОР
КОГДА Т1.ВремяНачалаИнтервала > Т2.ВремяНачалаИнтервала
ТОГДА Т1.ВремяНачалаИнтервала
ИНАЧЕ Т2.ВремяНачалаИнтервала
КОНЕЦ КАК ВремяНачалаПересечения,
ВЫБОР
КОГДА Т1.ВремяОкончанияИнтервала < Т2.ВремяОкончанияИнтервала
ТОГДА Т1.ВремяОкончанияИнтервала
ИНАЧЕ Т2.ВремяОкончанияИнтервала
КОНЕЦ КАК ВремяОкончанияПересечения
ИЗ
ВТ_Интервалы КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Интервалы КАК Т2
// Исключаем сравнение интервала с самим собой и убираем зеркальные пары
ПО (Т1.Смена < Т2.Смена
ИЛИ (Т1.Смена = Т2.Смена И Т1.НомерСтрокиИнтервала < Т2.НомерСтрокиИнтервала))
// Корректное условие наложения интервалов
И Т1.ВремяНачалаИнтервала < Т2.ВремяОкончанияИнтервала
И Т2.ВремяНачалаИнтервала < Т1.ВремяОкончанияИнтервала
Рассмотрим подробнее условие связи (Т1.Смена < Т2.Смена ИЛИ (Т1.Смена = Т2.Смена И Т1.НомерСтрокиИнтервала < Т2.НомерСтрокиИнтервала)):
(Т1.Смена = Т2.Смена И Т1.НомерСтрокиИнтервала < Т2.НомерСтрокиИнтервала) позволяет находить внутренние пересечения строк табличной части в рамках одного и того же документа или элемента справочника.Если в реквизитах времени дата зафиксирована как служебная (например, 01.01.0001), интервалы с переходом через полночь (например, 22:15 – 02:15) содержат значение времени окончания меньше, чем время начала. В таких ситуациях прямое сравнение в запросе отработает некорректно.
Для корректного сопоставления выполним предварительную нормализацию таблицы интервалов одним из двух подходов:
ВремяОкончания < ВремяНачала, прибавляем к времени окончания один день с помощью функции ДОБАВИТЬКДАТЕ(ВремяОкончания, ДЕНЬ, 1).ОБЪЕДИНИТЬ ВСЕ разделяем ночной период на две строки: от времени начала до 23:59:59 и от 00:00:00 до времени окончания.Применение математически точных условий наложения и строгого порядка ключевых полей в секции ПО позволяет получить очищенный список пересекающихся интервалов без использования громоздких группировок и дополнительной программной постобработки в коде 1С.