В практике разработчика 1С часто возникает задача объединения двух независимых наборов данных в одну общую таблицу, где каждая строка представляет собой уникальный временной интервал. Типичный пример — объединение данных о состояниях сотрудника (отпуск, больничный) и данных о его графике работы или плановых начислениях. В этой статье мы подробно разберем, как реализовать такой механизм с помощью языка запросов 1С, проанализируем классические алгоритмы и рассмотрим современные возможности платформы.
Представим, что у нас есть две таблицы. В первой хранятся периоды по одному критерию, во второй — по другому. Нам нужно получить третью таблицу, в которой периоды «нарезаны» таким образом, чтобы внутри каждого отрезка данные из обеих исходных таблиц оставались неизменными. Основная сложность заключается в том, что даты начала и окончания в разных таблицах не совпадают, что требует создания новых «точек перелома» в итоговой хронологии.
Разберем ситуацию, когда во второй таблице встречаются специфические значения времени, например, 0:0:59. Это часто указывает на использование функций типа КонецМинуты(). При соединении таблиц важно учитывать такие микро-разрывы, чтобы не потерять данные в одну секунду или минуту.
Рассмотрим наиболее распространенный способ решения задачи через временные таблицы и левое соединение таблицы с самой собой — это решается через инструмент для отладки кода и выражений. Этот метод эффективен, когда нам нужно из набора дат начала сформировать полноценные периоды (Дата начала — Дата окончания).
Допустим, мы уже объединили все значимые даты из двух таблиц в одну временную таблицу ТаблицаДат с полями Сотрудник и ДатаНачала. Теперь нам нужно вычислить дату окончания для каждой записи. Проанализируем следующий код:
ВЫБРАТЬ
ТаблицаДатНачало.Сотрудник КАК Сотрудник,
ТаблицаДатНачало.ДатаНачала КАК ДатаНачала,
ЕСТЬNULL(ДОБАВИТЬКДАТЕ(МИНИМУМ(ТаблицаДатОкончание.ДатаНачала), СЕКУНДА, -1), ДАТАВРЕМЯ(3999, 12, 31)) КАК ДатаОкончания
ИЗ
ТаблицаДат КАК ТаблицаДатНачало
ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаДат КАК ТаблицаДатОкончание
ПО ТаблицаДатНачало.Сотрудник = ТаблицаДатОкончание.Сотрудник
И ТаблицаДатНачало.ДатаНачала < ТаблицаДатОкончание.ДатаНачала
СГРУППИРОВАТЬ ПО
ТаблицаДатНачало.Сотрудник,
ТаблицаДатНачало.ДатаНачала
Разберем по шагам, что происходит в этом запросе:
ТаблицаДатНачало.ТаблицаДатОкончание) по условию, что дата во второй таблице строго больше даты в первой.МИНИМУМ, мы находим «следующую» дату начала, которая и станет границей для текущего периода.ДОБАВИТЬКДАТЕ(..., СЕКУНДА, -1).ЕСТЬNULL.Если задача стоит шире — объединить две таблицы с разными данными в одну шкалу, рекомендуется использовать четырехшаговый алгоритм «сбора точек перелома».
Шаг 1: Сбор всех границ. Выбираем все даты начала из Таблицы 1 и все даты начала из Таблицы 2. Объединяем их через ОБЪЕДИНИТЬ ВСЕ во временную таблицу.
Шаг 2: Получение уникальных моментов времени. Схлопываем дубликаты дат с помощью СГРУППИРОВАТЬ ПО. Теперь у нас есть полный список моментов, когда в системе происходило любое изменение (хоть по первой таблице, хоть по второй).
Шаг 3: Формирование «пустых» интервалов. Используем метод самосоединения, описанный выше, чтобы превратить список дат в список периодов [ДатаНачала, ДатаОкончания].
Шаг 4: Наполнение данными. К полученной сетке периодов левым соединением подтягиваем данные из исходных таблиц. Условие соединения будет выглядеть так:
ЛЕВОЕ СОЕДИНЕНИЕ ИсходнаяТаблица1 КАК Таб1
ПО Сетка.Сотрудник = Таб1.Сотрудник
И Сетка.ДатаНачала >= Таб1.ДатаНачала
И Сетка.ДатаОкончания <= Таб1.ДатаОкончания
Важно помнить: если в исходных таблицах периоды могут иметь «дырки», условие соединения нужно проектировать аккуратно, возможно, используя дополнительные временные таблицы для хранения «срезов последних» на каждую точку перелома.
Выясним причину, почему в ЗУП 3.1 эта задача встречается особенно часто. Конфигурация построена на механизме состояний сотрудников. Если вам нужно соединить, например, Данные состояний сотрудников и какой-либо регистр расчетов, помните о следующем:
ЗарплатаКадрыОбщие позволяют создавать временные таблицы с периодами автоматически.НАЧАЛОПЕРИОДА, если точное время не имеет значения для учета.Проанализируем ситуацию с развитием платформы. Начиная с версии 8.3.21, в языке запросов появились оконные функции, которые позволяют решить задачу получения «следующей даты» без тяжелого самосоединения LEFT JOIN.
Рассмотрим пример использования функции LEAD (в 1С это реализуется через СЛЕДУЮЩЕЕ в контексте оконных функций, если это поддерживается синтаксисом конкретной версии или через СКД):
В системе компоновки данных (СКД) можно использовать выражение:
ВычислитьВыражение("ДатаНачала", , , "Следующая", "ДатаНачала")
Это работает в разы быстрее на больших объемах данных (тысячи сотрудников и десятки тысяч записей), так как системе не приходится перемножать таблицу саму на себя.
Важный совет: Если вы работаете на старых версиях платформы или пишите код для тиражного решения с неопределенной версией сервера 1С, придерживайтесь Метода 1, так как он является наиболее совместимым и стабильным.