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