Как получить значения периодического регистра сведений на разные даты в одном запросе

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

При разработке на платформе 1С программисты часто сталкиваются с задачей получения данных из периодического регистра сведений не на текущий момент, а на определенные даты в прошлом. Ситуация усложняется, когда таких дат много — например, нам нужно получить подразделение или должность сотрудника на каждую дату формирования кадрового документа из списка. Стандартная виртуальная таблица СрезПоследних не позволяет передать в параметры поле из другой таблицы, поэтому приходится использовать механизмы запросов и временных таблиц.

В этой статье мы разберем, почему простой запрос с группировкой может не работать, и рассмотрим профессиональный алгоритм решения этой задачи.

Анализ проблемы: почему МАКСИМУМ(Период) не всегда срабатывает

Рассмотрим ситуацию, с которой столкнулся автор темы. В запросе выполняется соединение временной таблицы с датами и регистра сведений. Основная идея — найти максимальный период в регистре, который меньше или равен целевой дате. Однако, если в список полей СГРУППИРОВАТЬ ПО включить ресурс регистра (например, КостЦентр), запрос вернет все записи регистра, попадающие под условие Регистр.Период <= Дата, а не одну последнюю.

Проанализируем типичный ошибочный код:


ВЫБРАТЬ
    ОбщиеДанные.Сотрудник КАК Сотрудник,
    ОбщиеДанные.ДатаИзменения КАК ДатаИзменения,
    КостЦентрФизЛица.КостЦентр КАК КостЦентр,
    МАКСИМУМ(КостЦентрФизЛица.Период) КАК Период
ИЗ
    ОбщиеДанные КАК ОбщиеДанные
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КостЦентрФизЛица КАК КостЦентрФизЛица 
    ПО ОбщиеДанные.Сотрудник = КостЦентрФизЛица.Сотрудник 
    И ОбщиеДанные.ДатаИзменения >= КостЦентрФизЛица.Период
СГРУППИРОВАТЬ ПО
    ОбщиеДанные.Сотрудник,
    ОбщиеДанные.ДатаИзменения,
    КостЦентрФизЛица.Период, // ОШИБКА: Период не должен быть в группировке
    КостЦентрФизЛица.КостЦентр // ОШИБКА: Ресурс разбивает записи на разные группы

Почему это не работает: Группировка в языке запросов 1С работает так же, как в SQL. Если значение в поле КостЦентр менялось со временем, то для каждой смены значения СУБД создаст отдельную строку. Функция МАКСИМУМ(Период) в этом случае просто вернет период конкретной записи внутри группы, и мы получим «дубли» строк на одну и ту же дату.

Правильный алгоритм: «Срез последних на каждую дату» по шагам

Для надежного получения данных используется трехшаговая схема с применением временных таблиц. Это позволяет четко разделить логику поиска нужного момента времени и получение самих значений.

Шаг 1. Получение максимальных дат (периодов)

Сначала мы должны определить для каждой пары «Объект + Целевая дата» тот самый единственный Период в регистре, который является актуальным. На этом этапе мы выбираем только ключевые поля и период, исключая ресурсы (такие как подразделение, сумма, склад и т.д.).


ВЫБРАТЬ
    ВТ_Даты.Сотрудник КАК Сотрудник,
    ВТ_Даты.ДатаСреза КАК ДатаСреза,
    МАКСИМУМ(Регистр.Период) КАК ПериодДляСреза
ПОМЕСТИТЬ ВТ_МаксимальныеПериоды
ИЗ
    ВТ_Даты КАК ВТ_Даты
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КостЦентрФизЛица КАК Регистр
    ПО ВТ_Даты.Сотрудник = Регистр.Сотрудник
    И ВТ_Даты.ДатаСреза >= Регистр.Период
СГРУППИРОВАТЬ ПО
    ВТ_Даты.Сотрудник,
    ВТ_Даты.ДатаСреза
ИНДЕКСИРОВАТЬ ПО
    Сотрудник,
    ПериодДляСреза
;

Важный момент: Мы обязательно используем ИНДЕКСИРОВАТЬ ПО. Это критически важно для производительности последующего соединения, особенно если в таблице сотни или тысячи строк.

Шаг 2. Получение данных из регистра по найденным периодам

Теперь, когда у нас есть точные значения периодов, мы можем соединить нашу временную таблицу с самим регистром сведений. Соединение будет идти по условию «Равно», что работает максимально быстро.


ВЫБРАТЬ
    ВТ_МаксПериоды.Сотрудник КАК Сотрудник,
    ВТ_МаксПериоды.ДатаСреза КАК ДатаСреза,
    РегистрДанные.КостЦентр КАК КостЦентр
ИЗ
    ВТ_МаксимальныеПериоды КАК ВТ_МаксПериоды
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КостЦентрФизЛица КАК РегистрДанные
    ПО ВТ_МаксПериоды.Сотрудник = РегистрДанные.Сотрудник
    И ВТ_МаксПериоды.ПериодДляСреза = РегистрДанные.Период

Нюансы при работе с высокой периодичностью

Выясним причину возможных дублей, если периодичность регистра установлена в значение Позиция регистратора или в одну секунду было записано несколько движений. В этом случае одного МАКСИМУМ(Период) будет недостаточно, так как на одну секунду может прийтись две разные записи.

В таких ситуациях разберем дополнительные меры защиты:

  1. Если регистр имеет периодичность «Позиция регистратора», необходимо на первом шаге также получать МАКСИМУМ(Регистратор) или использовать связку с моментом времени.
  2. Если записи могут создаваться в одну секунду разными документами, добавьте в группировку и поиск максимума поле НомерСтроки (если оно есть в данном регистре).

Рекомендации по оптимизации

Проанализируем ситуацию с точки зрения нагрузки на сервер СУБД. Использование условия >= в соединении (неравенство) заставляет систему выполнять вложенные циклы. Чтобы запрос работал быстрее, придерживайтесь следующих правил:

Таким образом, мы выяснили, что основной ошибкой является попытка получить срез в одно действие через группировку по ресурсам. Использование временных таблиц для поэтапного нахождения максимального периода и последующего получения данных — это наиболее стабильный и быстрый способ решения задачи в 1С.

← На главную