Как получить родителя самого верхнего уровня в иерархическом справочнике 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При работе с иерархическими справочниками в 1С (например, Партнеры, Номенклатура или Подразделения) часто возникает задача быстрого определения "корневого" элемента для текущего объекта. Проблема усложняется тем, что глубина вложенности может быть заранее неизвестна, а стандартный язык запросов 1С не поддерживает рекурсивные функции напрямую. В этой статье мы разберем несколько проверенных методов решения этой задачи (включая вывод элементов иерархии верхнего уровня на СКД): от оптимизации структуры данных до программных хитростей и сложных запросов. При необходимости можно воспользоваться готовым решением для вывода дерева элементов иерархического справочника в СКД.

Способ 1: Добавление реквизита «Родитель верхнего уровня»

Этот метод считается наиболее производительным и "чистым" с точки зрения скорости работы отчетов. Если ваша конфигурация позволяет вносить изменения (напрямую или через расширение), мы рекомендуем добавить специальный реквизит ПартнерВерхнегоУровня в справочник. Это обеспечит корректное получение значения реквизита по ссылке даже на самых глубоких уровнях вложенности.

Рассмотрим логику реализации по шагам:

  1. Создаем в расширении или основной конфигурации реквизит справочника с типом ссылки на этот же справочник.
  2. В модуле объекта или через подписку на событие ПриЗаписи реализуем алгоритм поиска корня — в этом поможет универсальный обработчик событий при записи объектов без изменения конфигурации.
  3. В запросах просто обращаемся к этому реквизиту через точку, что исключает необходимость сложных соединений.

Пример программного кода для заполнения такого реквизита:


Процедура ПередЗаписью(Отказ)
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    // Если родителя нет, то верхний уровень — это сам элемент
    Если Не ЗначениеЗаполнено(Родитель) Тогда
        ПартнерВерхнегоУровня = Ссылка;
    Иначе
        // Рекурсивно или циклом находим корень
        ТекущийРодитель = Родитель;
        Пока ЗначениеЗаполнено(ТекущийРодитель.Родитель) Цикл
            ТекущийРодитель = ТекущийРодитель.Родитель;
        КонецЦикла;
        ПартнерВерхнегоУровня = ТекущийРодитель;
    КонецЕсли;
КонецПроцедуры

Преимущества: мгновенное получение данных в любом запросе или СКД без потери производительности, что особенно важно.

Способ 2: Использование итогов в запросе (Метод «ИТОГИ ПО»)

Если изменять структуру справочника нельзя, можно воспользоваться интересной особенностью работы платформы с иерархическими итогами. При использовании конструкции ИТОГИ ПО ... ТОЛЬКО ИЕРАРХИЯ 1С выстраивает дерево элементов, где самым первым элементом в выборке всегда будет корень иерархии.

Разберем, как это реализовать программно:


Функция ПолучитьГоловногоПартнера(ВыбранныйПартнер) Экспорт
    // Проверка на пустую ссылку или корень
    Если Не ЗначениеЗаполнено(ВыбранныйПартнер) Или Не ЗначениеЗаполнено(ВыбранныйПартнер.Родитель) Тогда
        Возврат ВыбранныйПартнер;
    КонецЕсли;

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Партнеры.Ссылка КАК Ссылка
        |ИЗ
        |   Справочник.Партнеры КАК Партнеры
        |ГДЕ
        |   Партнеры.Ссылка = &Ссылка
        |ИТОГИ ПО
        |   Ссылка ТОЛЬКО ИЕРАРХИЯ";

    Запрос.УстановитьПараметр("Ссылка", ВыбранныйПартнер);
    Выборка = Запрос.Выполнить().Выбрать();

    // Первый вызов Следующий() вернет самый верхний уровень иерархии
    Если Выборка.Следующий() Тогда
        Возврат Выборка.Ссылка;
    КонецЕсли;

    Возврат ВыбранныйПартнер;
КонецФункции

Важный нюанс: этот метод эффективен для получения родителя одного конкретного объекта. Его сложно использовать внутри большого запроса, объединяющего тысячи записей продаж.

Способ 3: Транзитивное замыкание (пакетный запрос)

Проанализируем ситуацию, когда нам нужно получить корень иерархии для множества записей прямо в тексте запроса без обращения к коду 1С. Для этого применяется метод "транзитивного замыкания". Чтобы не писать такие конструкции вручную, можно использовать интерактивное формирование текста запроса для получения родителей всех уровней — для этого подойдёт инструмент для тестирования и отладки запросов и СКД.

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

Рассмотрим пример построения такого запроса для 3-х уровней (принцип аналогичен для большего количества):


ВЫБРАТЬ
    Продажи.Партнер КАК ИсходныйПартнер,
    ЕСТЬNULL(Уровень3.Ссылка, ЕСТЬNULL(Уровень2.Ссылка, ЕСТЬNULL(Уровень1.Ссылка, Продажи.Партнер))) КАК Корень
ИЗ
    РегистрНакопления.Продажи.Обороты КАК Продажи
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Партнеры КАК Уровень1
        ПО Продажи.Партнер.Родитель = Уровень1.Ссылка
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Партнеры КАК Уровень2
        ПО Уровень1.Родитель = Уровень2.Ссылка
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Партнеры КАК Уровень3
        ПО Уровень2.Родитель = Уровень3.Ссылка
ГДЕ
    // Здесь можно добавить фильтр, чтобы оставить только те записи, 
    // где у найденного элемента Родитель является пустой ссылкой

Проанализируем ситуацию: хотя этот метод кажется громоздким, он позволяет в рамках одного запроса получить корневые элементы для всего отчета. При работе с большими данными также стоит учитывать оптимизацию отчетов с набором данных - объект, чтобы избежать проблем с производительностью.

Способ 4: Использование дополнительных сведений или регистров

Если вы работаете в системе, где изменение справочников запрещено (типовая конфигурация на поддержке без возможности расширения), можно использовать механизм Дополнительных сведений.

Рассмотрим алгоритм решения:

  1. Создаем дополнительное сведение "Головная организация" для справочника Партнеры.
  2. Пишем небольшую внешнюю обработку, которая один раз (или регламентно) обходит справочник и записывает в это свойство ссылку на корень.
  3. В запросах подключаем таблицу РегистрСведений.ДополнительныеСведения.

Также в некоторых конфигурациях (например, 1С:ЗУП или 1С:Документооборот) уже существуют системные регистры сведений, такие как ПодчиненностьПодразделений. Проверьте, нет ли аналогичного регистра для вашего справочника — он позволяет получить корень прямым запросом к одной таблице.

Подведем итоги

Выбор метода зависит от ваших ограничений:

  1. Если есть возможность менять конфигурацию — выбирайте Способ 1 (новый реквизит). Это обеспечит максимальную скорость работы системы в будущем.
  2. Если нужно быстро получить результат в коде для одного элемента — используйте Способ 2 (запрос с итогами).
  3. Если вы строите сложный отчет в СКД и не можете менять структуру — применяйте Способ 3 (несколько соединений в запросе) или специализированный вывод элементов иерархии верхнего уровня на СКД.

Помните, что работа с иерархией — это всегда нагрузка на сервер СУБД, поэтому старайтесь кешировать результаты поиска корня везде, где это возможно.

← На главную