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

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При работе с многоуровневыми иерархическими справочниками в 1С часто возникает задача: для произвольного элемента определить его самого верхнего родителя, то есть корневой элемент его ветки. Это может понадобиться для фильтрации, построения отчетов или для реализации специфической бизнес-логики. Давайте разберем несколько эффективных способов решения этой задачи как для старой платформы 1С 7.7, так и для современных версий 1С 8.х.

Решение 1: Итеративный подъем по иерархии (Цикл)

Самый простой и надежный способ — это последовательный подъем вверх по иерархии от текущего элемента к его родителям до тех пор, пока мы не достигнем самого верхнего уровня. Логика здесь элементарна: мы берем текущий элемент, проверяем, есть ли у него родитель. Если есть, то этот родитель становится нашим новым текущим элементом, и мы повторяем проверку. Цикл завершается, когда мы доходим до элемента, у которого родителя уже нет.

Этот подход универсален и хорошо работает как на старых, так и на новых платформах, отличаясь лишь синтаксисом.

Пример для 1С 7.7

В "семерке" для проверки пустого значения используется функция ПустоеЗначение(). Код может выглядеть так:


// Предположим, что в переменной "Элемент" у нас ссылка на исходный элемент справочника
Папа = Элемент.Родитель;

// Запускаем цикл, который будет работать, пока у "Папы" есть свой родитель
Пока ПустоеЗначение(Папа.Родитель) = 0 Цикл
    // Перемещаемся на один уровень вверх
    Папа = Папа.Родитель;
КонецЦикла;

// После завершения цикла в переменной "Папа" будет ссылка на родителя первого уровня
// Если у исходного элемента не было родителей, "Папа" будет содержать ссылку на него самого
Сообщить(Папа);

Пример для 1С 8.х

На платформе 8.х синтаксис становится более современным. Вместо ПустоеЗначение() используется метод ссылки .Пустая(). Часто эту логику оформляют в виде функции для переиспользования.


Функция ПолучитьРодителяПервогоУровня(СправочникСсылка)
    // Делаем копию ссылки, чтобы не изменять исходный параметр
    ТекущийЭлемент = СправочникСсылка;

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

// Пример вызова:
// ИсходныйЭлемент = Справочники.Номенклатура.НайтиПоКоду("00123");
// КорневойРодитель = ПолучитьРодителяПервогоУровня(ИсходныйЭлемент);
// Сообщить(КорневойРодитель);

Решение 2: Рекурсивный подход

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

Пример рекурсивной функции (для 1С 7.7)

Рассмотрим пример функции ДайПапу. Она проверяет, выбран ли родитель у текущего элемента. Если да, то она вызывает саму себя для этого родителя. Если нет — возвращает текущий элемент, который и является вершиной иерархии.


Функция ДайПапу(Элемент)
    // Проверяем, есть ли у элемента родитель и он не пустой
    Если Элемент.Родитель.Выбран() = 1 Тогда
        // Если родитель есть, вызываем эту же функцию для него
        Возврат ДайПапу(Элемент.Родитель);
    КонецЕсли;
    
    // Если родителя нет, значит, мы на вершине. Возвращаем текущий элемент.
    Возврат Элемент;
КонецФункции

// Пример использования:
// МойЭлемент = СоздатьОбъект("Справочник.Номенклатура");
// МойЭлемент.НайтиПоКоду("...");
// Корень = ДайПапу(МойЭлемент);

Решение 3: Использование запросов (для 1С 8.х)

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

Этот метод особенно эффективен при работе с большим объемом данных, так как все вычисления производятся на стороне сервера СУБД.

Пример запроса


Функция ПолучитьРодителяПервогоУровняЗапросом(ЭлементСсылка)
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    Номенклатура.Ссылка
        |ИЗ
        |    Справочник.Номенклатура КАК Номенклатура
        |ГДЕ
        |    Номенклатура.Ссылка = &Элемент
        |
        |ИТОГИ ПО
        |    Ссылка ТОЛЬКО ИЕРАРХИЯ";

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

    РезультатЗапроса = Запрос.Выполнить();

    // Обходим результат по группировкам, так как используется ИЕРАРХИЯ
    Выборка = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

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

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

Решение 4: Встроенные методы платформы 1С 7.7

Платформа 1С 7.7 предоставляет удобные встроенные методы для работы с иерархией, которые могут избавить от необходимости писать циклы или рекурсию.

  1. Метод ПолныйКод(): Возвращает строку, содержащую коды всех родительских элементов и код самого элемента, разделенные символом-разделителем (обычно `/` или `\`). Код родителя первого уровня будет первым в этой строке.
  2. Метод ПолноеНаименование(): Работает аналогично, но возвращает строку с наименованиями.

Пример использования ПолныйКод() в 1С 7.7


// Получаем ссылку на элемент
Элемент = СоздатьОбъект("Справочник.Номенклатура");
Элемент.НайтиПоНаименованию("Саморез 4.8*152");

// Получаем полный код
ПолныйКодЭлемента = Элемент.ПолныйКод(); // Например, "Группа1/ПодгруппаА/00123"

// Находим позицию первого разделителя
ПозицияРазделителя = Найти(ПолныйКодЭлемента, "/");

Если ПозицияРазделителя > 0 Тогда
    // Если разделитель найден, извлекаем код корневого родителя
    КодКорня = Лев(ПолныйКодЭлемента, ПозицияРазделителя - 1);
    
    // Находим элемент по этому коду
    Корень = СоздатьОбъект("Справочник.Номенклатура");
    Если Корень.НайтиПоКоду(КодКорня) = 1 Тогда
        Сообщить("Родитель первого уровня: " + Корень.Наименование);
    КонецЕсли;
Иначе
    Сообщить("Элемент сам является родителем первого уровня.");
КонецЕсли;

Альтернативные архитектурные подходы

В высоконагруженных системах, где поиск корневого элемента выполняется очень часто, можно рассмотреть варианты с денормализацией данных для ускорения доступа.

Эти подходы являются более сложными в реализации, но могут быть оправданы при высоких требованиях к производительности.

← На главную