При разработке отчетов и интеграций в конфигурациях зарплатно-кадрового блока разработчики часто сталкиваются с задачей получения актуальных кадровых данных сотрудника (подразделение, должность, табельный номер) вместе с его контактными данными (например, адресом электронной почты). Неоптимальный подход часто приводит к громоздким конструкциям: созданию цепочек временных таблиц, избыточным левым соединениям справочников только ради получения реквизита наименования и даже к декартову произведению строк при объединении выборок.
Разберем типичные ошибки в таких запросах, рассмотрим способы их оптимизации с помощью чистого языка запросов 1С и выясним причину, почему прямое обращение к срезу последних кадровой истории в типовых базах может возвращать некорректные результаты.
Проанализируем ситуацию на примере исходного запроса, где автор решает задачу отбора данных сотрудника по ИНН:
ВТ_КадровыеДанные КАК ВТ_КадровыеДанные, ВТ_ФизЛица КАК ВТ_ФизЛица). Без явного условия соединения в секции ГДЕ или оператора ПО это приводит к полному декартову произведению записей, что при наличии нескольких записей приведет к дублированию строк.Справочник.ПодразделенияОрганизаций, Справочник.Должности и Справочник.Сотрудники. При простом получении наименования или кода ссылки обращение через одну точку (например, КадроваяИсторияСотрудниковСрезПоследних.Подразделение.Наименование) транслируется платформой 1С в точно такое же неявное левое соединение, поэтому ручное раздувание текста запроса здесь не дает выигрыша в читаемости или производительности.ФизическиеЛица.ИНН = &ИИН находится в секции ГДЕ, а не во втором параметре виртуальной таблицы СрезПоследних(&Дата, ...). В результате СУБД сначала вычисляет срез по всей таблице кадровой истории на дату, и лишь затем отсекает строки, что крайне негативно сказывается на скорости работы.ВидСобытия <> ЗНАЧЕНИЕ(Перечисление.ВидыКадровыхСобытий.Увольнение) в секции ГДЕ среза последних полностью исключает уволенного сотрудника из выборки. Если человек уволен, срез вернет именно строку увольнения, а фильтр отсечет её, не показав никакой информации вообще.Рассмотрим, как можно свести весь исходный сценарий к одному лаконичному запросу без создания промежуточных временных таблиц в базе данных. Объединим кадровый срез и табличную часть контактной информации через физическое лицо.
Посмотрим на пример оптимизированного запроса:
ВЫБРАТЬ
тКи.ФизическоеЛицо.ИНН КАК СотрудникФизическоеЛицоИНН,
тКи.Подразделение.Наименование КАК division,
тКи.Должность.Наименование КАК position,
тКи.Сотрудник.Код КАК table_number,
ЕСТЬNULL(тИнфо.Представление, "") КАК Email
ИЗ
РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(
&Дата,
ФизическоеЛицо В (
ВЫБРАТЬ Ссылка
ИЗ Справочник.ФизическиеЛица
ГДЕ ИНН = &ИИН
)
) КАК тКи
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ФизическиеЛица.КонтактнаяИнформация КАК тИнфо
ПО тКи.ФизическоеЛицо = тИнфо.Ссылка
И тИнфо.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты)
И тИнфо.Вид = ЗНАЧЕНИЕ(Справочник.ВидыКонтактнойИнформации.EMailФизическиеЛица)
ГДЕ
тКи.ВидСобытия <> ЗНАЧЕНИЕ(Перечисление.ВидыКадровыхСобытий.Увольнение)
Разберем подробнее ключевые изменения в этой конструкции:
СрезПоследних через вложенный подзапрос к справочнику ФизическиеЛица. Это заставляет платформу рассчитывать срез только для нужного человека.ПО левого соединения, благодаря чему при отсутствии email запись по сотруднику не пропадет из результата.Выясним причину, почему регистр КадроваяИсторияСотрудников нельзя просто так опрашивать через СрезПоследних в конфигурациях уровня 1С:ЗУП 3, 1С:ERP или 1С:Комплексная автоматизация. В этих системах кадровые движения могут быть временными (например, временный кадровый перевод на период отпуска другого сотрудника).
Если срок временного перевода уже истек, срез последних все равно вернет запись этого перевода, так как по умолчанию физического движения «возврат на старую должность» отдельной датой в этот срез может не записаться. В таких конфигурациях кадровый срез строится интервально, и прямое чтение среза вернет неактуальные данные.
Если задача решается внутри типовой конфигурации, правильнее всего отказаться от ручных запросов к кадровым регистрам и использовать штатный общий модуль КадровыйУчет.
Разберем по шагам, как получить гарантированно верные данные сотрудника на дату:
// Сформируем массив полей, которые требуется получить
МассивПолей = Новый Массив;
МассивПолей.Добавить("Подразделение");
МассивПолей.Добавить("Должность");
МассивПолей.Добавить("ТабельныйНомер");
// Получим ссылку на сотрудника по его ИНН
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Сотрудники.Ссылка КАК Сотрудник
|ИЗ
| Справочник.Сотрудники КАК Сотрудники
|ГДЕ
| Сотрудники.ФизическоеЛицо.ИНН = &ИНН
| И НЕ Сотрудники.В архиве";
Запрос.УстановитьПараметр("ИНН", ИИН);
Выборка = Запрос.Выполнить().Выгрузить();
МассивСотрудников = Выборка.ВыгрузитьКолонку("Сотрудник");
// Вызываем штатный метод для получения актуального среза
КадровыеДанные = КадровыйУчет.КадровыеДанныеСотрудников(
Истина, // Только действующие сотрудники
МассивСотрудников,
МассивПолей,
КонецДня(&Дата)
);
Метод КадровыйУчет.КадровыеДанныеСотрудников автоматически корректно учитывает временные переводы, совместительство и кадровые интервалы на переданную дату.
Если же кадровые данные требуется задействовать внутри более сложного пакетного запроса, воспользуемся методом КадровыйУчет.СоздатьВТКадровыеДанныеСотрудников, который сформирует готовую временную таблицу внутри переданного менеджера временных таблиц.
Для гарантированного извлечения контактной информации также рекомендуется обращаться к модулю УправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъектов, что исключит ошибки в случаях, когда контактная информация хранится не в табличной части справочника, а в одноименном регистре сведений библиотеки стандартных подсистем (БСП).