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