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