В практике разработки на платформе 1С часто возникает задача проверки уникальности или поиска пересечений временных интервалов. Это актуально для графиков работы, кадровых документов, бронирования ресурсов или начислений. Когда документов становится много, стандартный двойной цикл (перебор "каждый с каждым") начинает работать неприемлемо долго, так как его сложность составляет O(N²). Проанализируем, как можно ускорить этот процесс, используя запросы, алгоритмы сортировки и механизмы платформы.
Самый эффективный способ решить задачу — переложить поиск на СУБД. Даже если у вас есть сложные условия (проверка на исправления, статусы), их можно предварительно отсеять. Разберем по шагам, как построить такой запрос.
Для начала проанализируем математическое условие пересечения двух интервалов [ДатаНачала1, ДатаОкончания1] и [ДатаНачала2, ДатаОкончания2]. Интервалы пересекаются, если выполняется условие:
ДатаНачала1 < ДатаОкончания2 И ДатаНачала2 < ДатаОкончания1
Рассмотрим реализацию через временные таблицы:
// Помещаем данные во временную таблицу для индексации
Запрос.Текст =
"ВЫБРАТЬ
| Данные.Сотрудник КАК Сотрудник,
| Данные.ДатаНачала КАК ДатаНачала,
| Данные.ДатаОкончания КАК ДатаОкончания,
| Данные.Ссылка КАК Ссылка
|ПОМЕСТИТЬ ВТ_Документы
|ИЗ
| &ТаблицаДокументов КАК Данные
|ГДЕ
| НЕ Данные.ПометкаУдаления
| И НЕ Данные.ЭтоИсправление // Отсекаем лишнее сразу
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ИНДЕКСИРОВАТЬ ПО
| Сотрудник,
| ДатаНачала,
| ДатаОкончания
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Т1.Ссылка КАК Документ1,
| Т2.Ссылка КАК Документ2
|ИЗ
| ВТ_Документы КАК Т1
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Документы КАК Т2
| ПО Т1.Сотрудник = Т2.Сотрудник
| И Т1.ДатаНачала < Т2.ДатаОкончания
| И Т2.ДатаНачала < Т1.ДатаОкончания
| И Т1.Ссылка < Т2.Ссылка"; // Условие Т1.Ссылка < Т2.Ссылка исключает дубли и сравнение документа с самим собой
Использование МенеджерВременныхТаблиц и обязательное создание индексов по полям поиска позволяет СУБД использовать Nested Loops или Hash Join, что в разы быстрее обычного перебора в коде.
Если данные необходимо обрабатывать непосредственно в памяти (в коллекции ТаблицаЗначений), и их количество измеряется тысячами, лучше использовать алгоритм со сложностью O(N log N). Проанализируем его суть: вместо сравнения каждого документа с каждым, мы "двигаемся" по временной шкале.
Разберем алгоритм по шагам:
Этот метод исключает лишние сравнения. Мы сравниваем только те документы, которые действительно находятся в одной временной точке.
Если вы все же предпочитаете работать с ТаблицаЗначений и циклами, давайте выясним причину медленной работы. Основная проблема в том, что внутренний цикл всегда проходит по всей таблице. Это можно исправить, добавив сортировку и условие досрочного прерывания.
Посмотрим на пример реализации:
// 1. Сортируем таблицу по объекту (Сотруднику) и дате начала
ТаблицаДанных.Сортировать("Сотрудник, ДатаНачала");
КолличествоСтрок = ТаблицаДанных.Количество();
Для Ит = 0 По КолличествоСтрок - 1 Цикл
ТекущаяСтрока = ТаблицаДанных[Ит];
// 2. Внутренний цикл начинаем со следующей строки
Для ВнутрИт = Ит + 1 По КолличествоСтрок - 1 Цикл
СледующаяСтрока = ТаблицаДанных[ВнутрИт];
// Если перешли к другому сотруднику - пересечений больше не будет
Если СледующаяСтрока.Сотрудник <> ТекущаяСтрока.Сотрудник Тогда
Прервать;
КонецЕсли;
// 3. Главная оптимизация: если дата начала следующего документа
// больше даты окончания текущего, то из-за сортировки
// все последующие документы точно не пересекутся с текущим.
Если СледующаяСтрока.ДатаНачала >= ТекущаяСтрока.ДатаОкончания Тогда
Прервать;
КонецЕсли;
// Если мы здесь - значит обнаружено пересечение
Сообщить("Документы пересекаются: " + ТекущаяСтрока.Ссылка + " и " + СледующаяСтрока.Ссылка);
КонецЦикла;
КонецЦикла;
Этот метод превращает сложность из квадратичной в близкую к линейной, так как внутренний цикл будет выполнять всего несколько итераций (только для реально пересекающихся или близких по времени документов).
Выясним, есть ли в платформе готовые инструменты для решения этой задачи. В 1С существуют РегистрыРасчета. Если ваша задача связана с начислениями зарплаты или учетом времени, использование этого механизма — самое правильное архитектурное решение.
Платформа 1С на уровне ядра оптимизирована для работы с периодами действия. При использовании таблиц ФактическийПериодДействия система автоматически вычисляет пересечения и вытеснения. Если вы записываете данные в регистр с включенным периодом действия, вы можете получить список пересечений простым запросом к виртуальной таблице, не изобретая собственные алгоритмы.
Проанализируем еще несколько моментов, которые могут влиять на скорость:
ДатаНачала и ДатаОкончания хранятся значения типа Дата с точностью до секунды. Если сравнивать даты с разной детализацией, индексы могут работать неэффективно.ТаблицаЗначений можно создавать индексы через ТаблицаЗначений.Индексы.Добавить(). Это может ускорить поиск методом НайтиСтроки(), но для поиска пересечений (диапазонов) сортировка все равно эффективнее.Подводя итог, можно сказать: для максимальной скорости используйте индексированные временные таблицы в запросах. Если же логика проверок слишком сложна для SQL и требует объектной модели 1С — применяйте сортировку с досрочным выходом из цикла. Эти подходы позволят сократить время выполнения обработки с нескольких минут до нескольких секунд.