Как в запросе 1С получить предыдущие значения кадровых данных?

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

При разработке отчетов по кадровым перемещениям (особенно в конфигурациях типа ЗУП 2.5) часто возникает задача вывести данные в формате «было — стало». Например, если сотрудника перевели из одного подразделения в другое, в одной строке отчета нужно показать и старое подразделение, и новое. В этой статье мы подробно разберем, как реализовать получение предыдущих значений программно с помощью механизмов запросов 1С.

Метод 1: Классическая схема «Трех шагов» через временные таблицы

Это самый надежный и производительный способ, который работает во всех версиях платформы 1С:Предприятие 8. Суть метода заключается в том, чтобы сначала собрать все текущие события, затем найти для каждого события дату предыдущего изменения и, наконец, соединиться с регистром сведений по этим датам.

Шаг 1. Подготовка основных данных

Сначала мы выберем все кадровые перемещения за нужный период во временную таблицу. Нам обязательно понадобятся поля Сотрудник и Период (дата документа). Рассмотрим пример запроса:


ВЫБРАТЬ
    Работники.Сотрудник КАК Сотрудник,
    Работники.Период КАК Период,
    Работники.ПодразделениеОрганизации КАК ПодразделениеНовое,
    Работники.Должность КАК ДолжностьНовая
ПОМЕСТИТЬ ВТ_ОсновныеДанные
ИЗ
    РегистрСведений.РаботникиОрганизаций КАК Работники
ГДЕ
    Работники.Период МЕЖДУ &ДатаНачала И &ДатаКонца
;

Шаг 2. Поиск даты предыдущего значения

Теперь нам нужно для каждой строки из ВТ_ОсновныеДанные найти запись в этом же регистре, которая была актуальна непосредственно перед текущей. Мы проанализируем ситуацию: нам нужна запись по тому же сотруднику, период которой строго меньше текущего периода документа. Из всех таких записей нас интересует самая «свежая» — то есть с максимальной датой.


ВЫБРАТЬ
    Основные.Сотрудник КАК Сотрудник,
    Основные.Период КАК ПериодТекущий,
    МАКСИМУМ(РегистрДо.Период) КАК ПериодПредыдущий
ПОМЕСТИТЬ ВТ_МаксимальныеДатыДо
ИЗ
    ВТ_ОсновныеДанные КАК Основные
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.РаботникиОрганизаций КАК РегистрДо
        ПО Основные.Сотрудник = РегистрДо.Сотрудник
            И Основные.Период > РегистрДо.Период
СГРУППИРОВАТЬ ПО
    Основные.Сотрудник,
    Основные.Период
;

Шаг 3. Получение итоговых данных

На финальном этапе мы соединяем наши основные данные с таблицей дат, а затем — с самим физическим регистром сведений, чтобы «вытащить» значения подразделения и должности на найденную предыдущую дату.


ВЫБРАТЬ
    Данные.Сотрудник,
    Данные.Период КАК ДатаПеремещения,
    Данные.ПодразделениеНовое,
    Данные.ДолжностьНовая,
    ЕСТЬNULL(ПредРегистр.ПодразделениеОрганизации, "---") КАК ПодразделениеСтарое,
    ЕСТЬNULL(ПредРегистр.Должность, "---") КАК ДолжностьСтарая
ИЗ
    ВТ_ОсновныеДанные КАК Данные
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_МаксимальныеДатыДо КАК Даты
        ПО Данные.Сотрудник = Даты.Сотрудник
            И Данные.Период = Даты.ПериодТекущий
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.РаботникиОрганизаций КАК ПредРегистр
        ПО Даты.Сотрудник = ПредРегистр.Сотрудник
            И Даты.ПериодПредыдущий = ПредРегистр.Период
;

Метод 2: Использование СКД (функция ВычислитьВыражение)

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

В СКД на закладке «Вычисляемые поля» или в ресурсах можно прописать выражение, которое обращается к предыдущей строке группировки. Рассмотрим синтаксис:

ВычислитьВыражение("Должность", , , "Предыдущая", "Текущая")

Преимущества этого подхода:

Важные нюансы при работе с периодическими регистрами

Проанализируем несколько критических моментов, которые могут исказить результаты вашего отчета в ЗУП 2.5 или других конфигурациях:

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

Метод 3: Использование коррелированных подзапросов (в соединении)

Хотя в 1С нельзя написать подзапрос прямо в поле ВЫБРАТЬ, мы можем использовать его в условии связи. Посмотрим на пример:


ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.РаботникиОрганизаций КАК ПредЗначения
ПО Данные.Сотрудник = ПредЗначения.Сотрудник
    И ПредЗначения.Период В (
        ВЫБРАТЬ ПЕРВЫЕ 1 Т.Период
        ИЗ РегистрСведений.РаботникиОрганизаций КАК Т
        ГДЕ Т.Сотрудник = Данные.Сотрудник
            И Т.Период < Данные.Период
        УПОРЯДОЧИТЬ ПО Т.Период УБЫВ
    )

Важное предупреждение: Данный метод следует использовать с осторожностью. На больших базах он может значительно замедлить выполнение запроса, так как СУБД придется выполнять подзапрос для каждой строки основного набора данных. Рассмотрим его как альтернативу для небольших выборок или специфических условий.

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

← На главную