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