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