Как в 1С получить самого верхнего родителя (корневую группу) и оптимизировать этот процесс

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

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

Проблема рекурсивного обхода в коде

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


Пока ЗначениеЗаполнено(Группа.Родитель) Цикл
    Группа = Группа.Родитель;
КонецЦикла;

Этот алгоритм кажется простым, но при обработке тысяч строк в результате запроса он становится "узким местом". Причина в том, что каждое обращение к свойству Родитель — это потенциальный отдельный запрос к базе данных (если объект не закэширован). Если у вас 10 000 строк и глубина вложенности 5 уровней, вы можете получить десятки тысяч обращений к серверу БД. Для оптимизации программного обхода рекомендуется использовать кэширование в памяти (объект Соответствие), где ключом будет ссылка на номенклатуру, а значением — найденная категория.

Решение 1: Запрос с фиксированной вложенностью

Если структура вашего справочника ограничена разумными пределами (например, не более 5-6 уровней), можно использовать конструкцию ВЫБОР КОГДА прямо в тексте запроса. Это позволит получить корень иерархии за один проход, не выполняя постобработку в коде.

Разберем пример такого запроса:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Товар,
    ВЫБОР
        КОГДА Номенклатура.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
            ТОГДА Номенклатура.Ссылка
        КОГДА Номенклатура.Родитель.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
            ТОГДА Номенклатура.Родитель
        КОГДА Номенклатура.Родитель.Родитель.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
            ТОГДА Номенклатура.Родитель.Родитель
        ИНАЧЕ Номенклатура.Родитель.Родитель.Родитель
    КОНЕЦ КАК КорневаяГруппа
ИЗ
    Справочник.Номенклатура КАК Номенклатура

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

Решение 2: Динамическое построение запроса (СхемаЗапроса)

Чтобы не ограничивать себя фиксированным количеством уровней, мы можем использовать объект СхемаЗапроса. Это позволит программно генерировать текст запроса с нужным количеством соединений. Проанализируем концепцию, где мы "пробиваем" иерархию на заданную глубину:


Функция ПолучитьТекстЗапросаИерархии(МаксимальнаяГлубина = 10)
    Схема = Новый СхемаЗапроса;
    // ... настройка источников и соединений ...
    // В цикле добавляем вложенные соединения таблицы самой с собой
    Для н = 1 По МаксимальнаяГлубина Цикл
        // Добавление логики выбора родителя следующего уровня
    КонецЦикла;
    Возврат Схема.ПолучитьТекстЗапроса();
КонецФункции

Такой подход объединяет преимущества первого решения (скорость выполнения на стороне сервера) и гибкость (возможность задать любую глубину). Однако стоит помнить, что чрезмерное количество соединений (более 20-30) может негативно сказаться на оптимизаторе запросов SQL-сервера.

Решение 3: Денормализация данных (Реквизит или Регистр)

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

Посмотрим, как это реализовать:

  1. Добавим в справочник Номенклатура реквизит КорневаяКатегория (тип Ссылка).
  2. В модуле объекта в обработчике ПередЗаписью пропишем логику поиска корня.
  3. При сохранении любого элемента система сама вычислит и запишет ссылку на его верхнюю группу.

Пример кода в модуле справочника:


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

Преимущества: В запросе отчета нам достаточно просто выбрать поле Номенклатура.КорневаяКатегория. Это работает мгновенно. Минусы: Нужно обновить данные для уже существующих записей и следить за актуальностью при перемещении элементов между группами.

Решение 4: Использование Системы компоновки данных (СКД)

Система компоновки данных обладает мощным встроенным механизмом работы с иерархией. Если ваша задача — вывести отчет, а не просто получить таблицу в коде, проанализируем возможности СКД:

  1. В наборе данных запроса укажите обычную ссылку на номенклатуру.
  2. В настройках структуры отчета добавьте группировку по полю Номенклатура с типом Иерархия.
  3. Если нужно программно получить данные, используйте вывод в ДеревоЗначений. В этом дереве узлы первого уровня и будут вашими корневыми категориями.

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

Решение 5: Метод "Логарифмического удвоения" в запросе

Это продвинутый метод, который позволяет найти родителя на глубине до 32 или 64 уровней всего за 5-6 соединений. Мы не просто соединяем таблицу с её родителем, а на каждом шаге соединяем результат с самим собой. На первом шаге получаем родителя (1 уровень), на втором — родителя родителя (2 уровень), на третьем — родителя четвертого уровня (2^2), на четвертом — восьмого уровня (2^3) и так далее. Это математически изящный способ "схлопнуть" иерархию любой сложности за минимальное количество операций.

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

Выясним, какой метод выбрать в зависимости от ситуации:

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

← На главную