Как в запросе 1С получить действующие плановые начисления на каждую дату кадрового движения?

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

В типовых конфигурациях 1С (таких как 1С:Зарплата и Управление Персоналом, УПП и Комплексная автоматизация) периодически возникает задача: для каждой строки кадровой истории сотрудника из регистра РаботникиОрганизаций вывести действующие на тот момент виды начислений и их размеры из регистра ПлановыеНачисленияРаботниковОрганизаций.

Сложность заключается в том, что виртуальная таблица СрезПоследних принимает параметр даты как единое константное значение на весь запрос. Передать в нее поле периода из ведущей кадровой таблицы построчно невозможно. Попытка использовать коррелированный подзапрос в секции ПО приводит к серьезным просадкам производительности СУБД и ошибкам выборки, если у сотрудника действует одновременно несколько видов начислений. Разберем, как правильно реализовать срез последних на каждую дату в рамках одного запроса.

Почему исходный запрос работает неэффективно и некорректно

Проанализируем типовые ошибки, которые часто допускаются при ручном соединении двух периодических регистров сведений:

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

Решение через цепочку временных таблиц

Посмотрим на классический и наиболее производительный способ решения задачи на языке запросов 1С. Разделим алгоритм на три логических шага.

  1. Шаг 1: Разворачивание основных движений и движений завершения во временную таблицу. Объединяем строки начала действия начислений со строками их окончания (где заполнен ПериодЗавершения).
  2. Шаг 2: Поиск максимальных дат на дату каждого кадрового события. Левым соединением связываем кадровую таблицу с начислениями по условию Начисления.Период <= Кадры.Период и группируем по сотруднику, кадровому периоду и виду расчета с функцией МАКСИМУМ(ПериодНачисления).
  3. Шаг 3: Получение актуальных реквизитов и отсечение прекращенных начислений. Соединяем полученные максимальные даты обратно со всеми движениями по равенству трех ключевых полей (Сотрудник, ВидРасчета, Период) и отфильтровываем записи со статусом «Прекратить».

Рассмотрим текст оптимизированного запроса:


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

ОБЪЕДИНИТЬ ВСЕ

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

ИНДЕКСИРОВАТЬ ПО
    Сотрудник,
    ВидРасчета,
    Период
;

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

ИНДЕКСИРОВАТЬ ПО
    Сотрудник,
    ВидРасчета,
    ПериодНачисления
;

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

Важные нюансы и рекомендации по производительности

Разберем ключевые детали, влияющие на скорость и корректность выборки:

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