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