Как найти пересечение периодов в запросе 1С и исключить дублирование записей?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С часто возникает задача проверки наложения временных интервалов. Это может быть проверка пересечения графиков отпусков, наложение больничных листов или поиск конфликтующих записей в регистрах сведений. Основная сложность заключается не только в самой формуле пересечения, но и в том, что при соединении таблицы самой с собой (self-join) система выдает «зеркальные» дубли: пары Документ1-Документ2 и Документ2-Документ1. Рассмотрим подробнее, как составить эффективный запрос, который решит обе эти проблемы.

Анализируем проблему зеркальных дублей

Когда мы пытаемся найти пересечения в рамках одной таблицы, мы обычно используем внутреннее соединение этой таблицы с ее копией по определенному условию (например, по сотруднику). Если условие пересечения выполняется для двух разных документов, запрос вернет две строки, так как для системы запись А пересекается с записью Б, а запись Б — с записью А.

Для того чтобы в итоговой выборке осталась только одна уникальная пара, проанализируем ситуацию с использованием сравнения ссылок. В языке запросов 1С ссылки можно сравнивать операторами «больше» или «меньше». Это сравнение происходит по внутреннему идентификатору (GUID). Добавив условие ТабДокументы.Ссылка < ТабДокументы1.Ссылка, мы гарантируем, что каждая пара встретится в результате только один раз.

Универсальная формула пересечения периодов

Многие разработчики используют громоздкие конструкции с оператором МЕЖДУ и множеством условий ИЛИ, чтобы поймать все варианты наложения (начало одного периода внутри другого, окончание внутри другого, полное поглощение и т.д.). Однако существует максимально простая математическая формула, которая покрывает 100% случаев пересечения.

Разберем по шагам логику: два периода [S1, E1] и [S2, E2] пересекаются тогда и только тогда, когда начало первого периода меньше или равно концу второго, И начало второго периода меньше или равно концу первого.

Запишем это в виде условия запроса:


| ПО ТабДокументы.Сотрудник = ТабДокументы1.Сотрудник
|    И ТабДокументы.ДатаНачала <= ТабДокументы1.ДатаОкончания
|    И ТабДокументы1.ДатаНачала <= ТабДокументы.ДатаОкончания
|    И ТабДокументы.ДокументСсылка < ТабДокументы1.ДокументСсылка

Это условие является наиболее производительным и коротким. Оно автоматически учитывает случаи, когда один период полностью поглощает другой или когда они совпадают до секунды.

Вариант решения с использованием функции АВТОНОМЕРЗАПИСИ()

Если данные находятся во временной таблице и у них нет уникальных ссылок (например, это строки таблицы значений), посмотрим на пример использования функции АВТОНОМЕРЗАПИСИ(). Этот метод позволяет пронумеровать строки и использовать этот номер для исключения дублей, аналогично сравнению ссылок.

Сначала подготовим временную таблицу:


ВЫБРАТЬ
    Т.Сотрудник КАК Сотрудник,
    Т.ДатаНачала КАК ДатаНачала,
    Т.ДатаОкончания КАК ДатаОкончания,
    Т.Ссылка КАК Ссылка,
    АВТОНОМЕРЗАПИСИ() КАК НомерСтроки
ПОМЕСТИТЬ ВТ_Данные
ИЗ
    &ВходящаяТаблица КАК Т
;

Затем выполним соединение:


ВЫБРАТЬ
    Т1.Ссылка КАК Документ1,
    Т2.Ссылка КАК Документ2,
    Т1.Сотрудник КАК Сотрудник
ИЗ
    ВТ_Данные КАК Т1
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Данные КАК Т2
        ПО Т1.Сотрудник = Т2.Сотрудник
            И Т1.ДатаНачала <= Т2.ДатаОкончания
            И Т2.ДатаНачала <= Т1.ДатаОкончания
            И Т1.НомерСтроки < Т2.НомерСтроки

Тонкости работы с «граничными» секундами

Выясним причину, по которой иногда запрос может находить лишние пересечения. В 1С время хранится с точностью до секунды. Если один документ заканчивается в 23:59:59, а следующий начинается в 00:00:00 следующего дня, пересечения нет. Однако, если бизнес-логика предполагает, что документы не могут «касаться» друг друга краями (например, один сотрудник не может уволиться и приняться в одну и ту же секунду), следует использовать строгие неравенства < и > вместо <= и >=.

Также важно учитывать пустые даты. В некоторых конфигурациях пустая дата окончания означает «по настоящее время». Если ваш запрос может столкнуться с такими данными, проанализируем ситуацию и добавим обработку через ВЫБОР:


| ВЫБОР 
|     КОГДА Таб.ДатаОкончания = ДАТАВРЕМЯ(1, 1, 1) 
|     ТОГДА ДАТАВРЕМЯ(3999, 12, 31) 
|     ИНАЧЕ Таб.ДатаОкончания 
| КОНЕЦ

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

При работе с большими объемами данных в ВТ_Данные (более 1000 записей), поиск пересечений методом «каждый с каждым» может существенно замедлить работу системы. Чтобы этого избежать, выполним следующие рекомендации:

  1. Индексация: Всегда индексируйте поля, по которым строится связь. В нашем случае это Сотрудник. Если сотрудников много, а документов мало — это спасет ситуацию. Используйте конструкцию ИНДЕКСИРОВАТЬ ПО при создании временной таблицы.
  2. Предварительный фильтр: Старайтесь максимально ограничить выборку по периоду (например, текущий месяц) перед помещением данных во временную таблицу.
  3. Использование специализированных функций: Если вы работаете в конфигурации на базе БСП или в ЗУП 3.1, проверьте наличие программных механизмов для проверки пересечений. Часто в общих модулях уже реализованы методы, которые делают это оптимальнее на уровне встроенного языка или специализированных запросов к регистрам.

Таким образом, комбинируя универсальную формулу пересечения и сравнение ссылок, мы получаем надежный и быстрый инструмент для анализа временных интервалов в 1С.

← На главную