Как получить подразделение сотрудника на каждый день в ЗУП 3.1 без пустых значений?

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

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

Анализ типичных ошибок в запросе

Проанализируем ситуацию, с которой сталкиваются многие разработчики. Попытка создать отдельные временные таблицы для каждого вида кадрового события (Прием, Перемещение и т.д.), а затем соединить их с календарем, — это избыточный путь. Основная проблема здесь заключается в возникновении значений NULL там, где на конкретную дату не было зафиксировано именно того события, по которому наложен фильтр.

Выясним причину: в конфигурации ЗУП 3.1 реализован механизм интервальных регистров. Регистр КадроваяИсторияСотрудниковИнтервальный уже содержит в себе «схлопнутые» данные. Если сотрудник принят в подразделение А и через год переведен в подразделение Б, в этом регистре будут две записи, которые покрывают весь период работы без разрывов. Первая запись будет иметь ДатаОкончания, равную дате перед переводом, а вторая — ДатаОкончания в далеком будущем (31.12.3999).

Важный момент: разделяя данные по ВидСобытия, мы сами создаем «дырки» в истории, которые потом героически пытаемся закрыть с помощью ОБЪЕДИНИТЬ или сложных условий ВЫБОР. Вместо этого нам нужно использовать свойство непрерывности интервального регистра.

Решение 1: Правильное использование интервального регистра в запросе

Рассмотрим, как упростить запрос и избавиться от NULL. Нам достаточно соединить заранее подготовленную таблицу дат с регистром кадровой истории всего один раз. Посмотрим на пример оптимизированного подхода:

  1. Генерируем список дат за период (используя временную таблицу или производственный календарь).
  2. Присоединяем РегистрСведений.КадроваяИсторияСотрудниковИнтервальный по условию вхождения даты в интервал.

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


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

Проанализируем это условие. Так как ДатаОкончания в интервальном регистре включительная, условие МЕЖДУ отрабатывает корректно. На каждый день периода для каждого сотрудника найдется ровно одна запись из регистра, и никаких NULL в поле Подразделение не возникнет, так как интервалы в ЗУП 3.1 строятся непрерывно с даты приема по дату увольнения.

Решение 2: Использование типовых функций ЗУП 3.1

Разберем более продвинутый и надежный способ. В ЗУП 3.1 архитекторы предусмотрели программный интерфейс для получения кадровых данных, который учитывает множество нюансов: исправления документов, работу по совместительству, специфику территориальных условий и т.д. Вместо написания сложного запроса вручную, мы можем воспользоваться методами общих модулей.

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

Рассмотрим пример вызова такой функции в коде:


ПараметрыПолученияДанных = УчетРабочегоВремени.ПараметрыДляСозданияВТДанныеУчетаВремениИСостоянийСотрудников();
ПараметрыПолученияДанных.ДатаНачала = НачалоПериода;
ПараметрыПолученияДанных.ДатаОкончания = КонецПериода;
ПараметрыПолученияДанных.СписокСотрудников = МассивСотрудников;

УчетРабочегоВремени.СоздатьВТДанныеУчетаВремениИСостоянийСотрудников(Запрос.МенеджерВременныхТаблиц, Истина, ПараметрыПолученияДанных);

После выполнения этого кода в менеджере временных таблиц вашего запроса появится таблица ВТДанныеУчетаВремениИСостоянийСотрудников. Она содержит поле Дата и все необходимые кадровые реквизиты. Это исключает необходимость вручную бороться с NULL и дублями при кадровых переводах внутри одного дня.

Решение 3: Оптимизация получения иерархии подразделений

Часто в запросе требуется вывести не только непосредственное подразделение, но и «головное» (верхнего уровня). Рассмотрим ситуацию из вопроса автора: использование конструкции Итог.Подразделение.Родитель. Проанализируем, почему это плохо: в запросе это приводит к неявному левому соединению с таблицей справочника ПодразделенияОрганизаций. Если уровней иерархии много, производительность падает.

Для оптимизации рекомендуем использовать реквизит ГоловноеПодразделение, если он предусмотрен в вашей логике учета, или предварительно подготовить временную таблицу со срезом структуры подразделений. Если же нужно получить именно «родителя», лучше делать это через явное левое соединение во временной таблице Итог, чтобы избежать многократных обращений через точку.

Советы по производительности

При работе с большими объемами данных (более 50-100 сотрудников) генерация дат через декартово произведение (как в исходном запросе автора) становится узким местом. Рассмотрим альтернативы:

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

Таким образом, мы выяснили, что для решения задачи «подразделение на каждый день» в ЗУП 3.1 не нужно собирать данные по крупицам из разных документов. Достаточно использовать интервальный регистр кадровой истории или воспользоваться мощным типовым механизмом учета рабочего времени, который сделает всю сложную работу за вас.

← На главную