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