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