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

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

В разработке на платформе 1С:Предприятие 8 часто возникает задача: для каждой строки из списка документов (или таблицы значений) необходимо подобрать актуальное значение из периодического регистра сведений. Например, для списка документов «Начисление зарплаты» нужно получить актуальные оклады сотрудников или их состояния на моменты времени каждого конкретного документа.

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

Почему нельзя использовать стандартный СрезПоследних в цикле или соединении?

Рассмотрим типичную ошибку начинающего разработчика. Попытка вызвать метод РегистрыСведений.Название.ПолучитьПоследнее() в цикле перебора выборки документов приведет к катастрофическому падению производительности. Если в списке 1000 документов, система выполнит 1000 отдельных запросов к базе данных. Это «антипаттерн», которого следует избегать.

Вторая ошибка — попытка соединить таблицу документов с виртуальной таблицей СрезПоследних в одном запросе. Виртуальная таблица принимает параметр &Период, который должен быть константой. Мы не можем написать в условии связи что-то вроде ГДЕ СрезПоследних.Период = ТаблицаДокументов.Дата, так как виртуальная таблица строится до того, как выполняется основное соединение запроса, и она построится на одну дату, а не на все даты из таблицы документов.

Алгоритм решения задачи: Метод «Соединение — Группировка — Соединение»

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

Шаг 1: Подготовка исходных данных во временной таблице

Проанализируем ситуацию: сначала нам нужно собрать все документы, для которых мы ищем данные, в одну временную таблицу. Это позволит нам проиндексировать поля связи и ускорить поиск.


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

Важный момент: индексация полей, по которым будет происходить связь с регистром (измерения + дата), критически важна для производительности на больших объемах данных.

Шаг 2: Получение максимальных дат из регистра

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


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

В этом запросе мы используем условие РегистрСостояния.Период <= ВТ_Документы.ДатаДокумента. Это позволяет нам отсечь все записи, сделанные «в будущем» относительно документа, а функция МАКСИМУМ оставит только ту дату, которая ближе всего к моменту документа «снизу».

Шаг 3: Получение финальных данных из регистра

На предыдущем шаге мы узнали когда было сделано последнее изменение. Теперь нам нужно узнать, какое именно это было изменение (получить ресурсы регистра, такие как «Состояние» или «Оклад»). Для этого мы снова соединяем результат с физической таблицей регистра, но теперь уже по точному совпадению периода и измерений.


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

Обратите внимание на использование ЛЕВОЕ СОЕДИНЕНИЕ. Если по какому-то сотруднику вообще не было записей в регистре до даты документа, внутреннее соединение просто выкинуло бы этот документ из итогового отчета. Левое соединение сохранит строку документа, просто заполнив поля состояния значением NULL.

Проблема неуникальности времени (несколько записей в одну секунду)

Проанализируем ситуацию, когда в регистре может быть несколько записей в пределах одной секунды. Если две разные записи имеют одинаковый Период, то группировка по максимуму периода не даст нам однозначного ответа, какую запись считать последней.

Для решения этой проблемы в платформе 1С существуют понятия Регистратор и НомерСтроки. В самых современных конфигурациях для этих целей рекомендуется использовать МоментВремени. Если точность до секунды вас не устраивает, алгоритм усложняется:

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

Однако для большинства бизнес-задач (кадровый учет, цены номенклатуры) точности до секунды бывает достаточно.

Оптимизация и важные нюансы

Рассмотрим несколько советов, которые помогут сделать ваш запрос профессиональным:

Резюме

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

← На главную