Как в 1С выбрать последнюю запись из регистра сведений по условию на неключевое поле?

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом IT и автоматизация бизнеса
← На главную

При разработке кадровых и учетных отчетов в 1С часто возникает задача получить актуальное состояние объекта не просто на шкале времени, а с учетом дополнительных условий. Классический пример: требуется выяснить постоянную должность сотрудника без учета временных кадровых переводов. В регистре сведений КадроваяИсторияСотрудников признаком временного перевода выступает заполненная дата окончания в поле ДействуетДо. Если попытаться применить стандартную виртуальную таблицу среза последних с простым условием, результат либо окажется пустым, либо платформа выдаст ошибку.

Разберем природу этого поведения платформы, выясним, почему типовой СрезПоследних не решает задачу напрямую, и подробно рассмотрим надежные способы построения запроса.

Почему стандартный СрезПоследних не работает с условием по ресурсу или реквизиту

Проанализируем две распространенные ошибки при попытке решить эту задачу через виртуальную таблицу:

  1. Передача условия в параметры виртуальной таблицы: если указать условие вида ДействуетДо = ДАТАВРЕМЯ(1, 1, 1) во втором параметре таблицы среза последних, конструктор или выполнение запроса выдаст ошибку Поле не найдено "ДействуетДо". Платформа 1С на уровне архитектуры СУБД формирует срез только по измерениям регистра. Накладывать фильтр в параметрах виртуальной таблицы разрешено исключительно на измерения. Реквизиты и ресурсы там недоступны.
  2. Накладывание условия в секции ГДЕ: если обратиться к срезу последних без параметров фильтрации, а условие ГДЕ ДействуетДо = ДАТАВРЕМЯ(1, 1, 1) написать в теле запроса, логика выборки нарушается. Платформа сначала находит физически последнюю запись на указанную дату (например, временный перевод от 03.03.2025). Затем к этой единственной выбранной строке применяется секция ГДЕ. Так как у этой строки поле ДействуетДо заполнено, фильтр отсекает ее, и запрос возвращает пустой результат вместо предыдущей постоянной строки со «Специалистом».

Также ошибочна попытка включить МАКСИМУМ(Период) и одновременно сгруппировать результат по всем полям, включая сам Период. В такой группировке каждая дата становится уникальной строкой, и отбор максимума по сотруднику теряет смысл.

Способ 1. Получение среза через временную таблицу с индексацией (рекомендуемый)

Рассмотрим классический и наиболее производительный алгоритм моделирования среза. Чтобы выбрать именно последнюю постоянную запись, разделим задачу на два шага: сначала найдем максимальную дату среди строк, удовлетворяющих нашему фильтру, а затем получим данные этой записи через внутреннее соединение.

Посмотрим на текст запроса:


// Шаг 1. Находим максимальные даты документов, удовлетворяющих условию
ВЫБРАТЬ
    КадроваяИстория.Сотрудник КАК Сотрудник,
    МАКСИМУМ(КадроваяИстория.Период) КАК Период
ПОМЕСТИТЬ ВТ_МаксимальныеПериоды
ИЗ
    РегистрСведений.КадроваяИсторияСотрудников КАК КадроваяИстория
ГДЕ
    КадроваяИстория.ДействуетДо = ДАТАВРЕМЯ(1, 1, 1)
    И КадроваяИстория.Период <= &Период
СГРУППИРОВАТЬ ПО
    КадроваяИстория.Сотрудник
ИНДЕКСИРОВАТЬ ПО
    Сотрудник,
    Период
;

// Шаг 2. Соединяем с исходным регистром для получения должности
ВЫБРАТЬ
    КадроваяИстория.Сотрудник КАК Сотрудник,
    КадроваяИстория.Должность КАК Должность,
    КадроваяИстория.Период КАК Период
ИЗ
    ВТ_МаксимальныеПериоды КАК ВТ
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КадроваяИсторияСотрудников КАК КадроваяИстория
        ПО ВТ.Сотрудник = КадроваяИстория.Сотрудник
        И ВТ.Период = КадроваяИстория.Период

Разберем важные детали этого решения:

  1. Мы обращаемся напрямую к физической таблице РегистрСведений.КадроваяИсторияСотрудников, а не к виртуальной. Это позволяет свободно фильтровать строки по реквизиту ДействуетДо.
  2. Инструкция ИНДЕКСИРОВАТЬ ПО создает индекс во временной таблице на стороне СУБД. При обработке сотен и тысяч сотрудников это исключает полное сканирование таблицы (Table Scan) и ускоряет работу запроса.
  3. Внимание к коллизиям: если регистр подчинен регистратору и в одну секунду по сотруднику проведено два кадровых документа, соединение только по полям Сотрудник и Период может дать задвоение строк. В таких конфигурациях в первую временную таблицу следует дополнительно выбирать МАКСИМУМ(Регистратор) или МАКСИМУМ(НомерСтроки) и связывать данные также по этим полям.

Способ 2. Отбор через коррелированный подзапрос в секции ГДЕ

Рассмотрим альтернативный вариант, который удобно использовать, когда нет возможности создавать цепочки временных таблиц (например, во вложенных запросах системы компоновки данных или простых динамических списках).

Посмотрим на пример реализации:


ВЫБРАТЬ
    КИ.Сотрудник КАК Сотрудник,
    КИ.Должность КАК Должность,
    КИ.Период КАК Период
ИЗ
    РегистрСведений.КадроваяИсторияСотрудников КАК КИ
ГДЕ
    (КИ.Сотрудник, КИ.Период) В
        (ВЫБРАТЬ
            КИ_Макс.Сотрудник,
            МАКСИМУМ(КИ_Макс.Период)
        ИЗ
            РегистрСведений.КадроваяИсторияСотрудников КАК КИ_Макс
        ГДЕ
            КИ_Макс.ДействуетДо = ДАТАВРЕМЯ(1, 1, 1)
            И КИ_Макс.Период <= &Период
        СГРУППИРОВАТЬ ПО
            КИ_Макс.Сотрудник)

Такой синтаксис проверяет кортеж полей (Сотрудник, Период) на вхождение в выборку максимальных дат. Однако на больших базах данных при значительном количестве сотрудников этот вариант уступает временным таблицам по скорости выполнения.

Особенности кадровых данных в типовых конфигурациях (ЗУП 3, ERP, КА)

Если задача решается в конфигурациях на базе современной БСП, важно помнить, что кадровая подсистема устроена сложнее простых периодических регистров:

  1. Интервальные структуры: временный перевод регистрируется одной записью со сроком окончания, но документ возврата на базовую должность после истечения срока в регистр не пишется. Для корректного определения актуального состояния сотрудника типовой функционал использует регистр КадроваяИсторияСотрудниковИнтервальный, где границы периодов рассчитываются автоматически.
  2. Типовое API: для получения срезов кадровой истории в коде типовых конфигураций рекомендуется вызывать функции общих модулей, например КадровыйУчет.СоздатьВТКадроваяИсторияСотрудников или методы модуля ЗарплатаКадрыПериодическиеРегистры, передавая массив сотрудников и требуемую дату. Это избавляет от необходимости вручную вычислять возвраты из временных переводов.
← На главную