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

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

При выгрузке данных во внешние системы (например, хранилища данных или OLAP-системы) часто возникает задача получить полный путь элемента в иерархии справочника в виде единой строки, например: Бытовая техника / Холодильники / Двухкамерные / Товар. Разберем, какие возможности и ограничения существуют в платформе 1С:Предприятие для решения этой задачи, а также рассмотрим оптимальные практические подходы.

Ограничения языка запросов 1С

Проанализируем возможности языка запросов платформы 1С. В отличие от некоторых диалектов SQL, где поддерживаются рекурсивные общие табличные выражения (WITH RECURSIVE) или специализированные функции конкатенации путей, язык запросов 1С имеет следующие особенности:

  1. Отсутствие динамической рекурсии: собрать путь произвольной глубины вложенности исключительно средствами одного запроса невозможно.
  2. Итоги по иерархии не формируют строку: использование конструкции ИТОГИ ПО Поле ИЕРАРХИЯ предназначено для группировки и суммирования ресурсов по узлам дерева, но не конкатенирует имена родителей в плоскую строку.
  3. Проблемы при разыменовании через точку: попытка обратиться к родителям вида Родитель.Родитель.Родитель генерирует каскадные соединения LEFT JOIN в СУБД, что жестко ограничивает глубину и отрицательно влияет на производительность запроса.

Способ 1. Фиксированное разыменование родителей в запросе с довыборкой

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

Рассмотрим пример формирования полей иерархии:


ВЫБРАТЬ
    Спр.Ссылка КАК Ссылка,
    Спр.Наименование КАК Наименование,
    Спр.Родитель КАК Родитель1,
    Спр.Родитель.Родитель КАК Родитель2,
    Спр.Родитель.Родитель.Родитель КАК Родитель3,
    Спр.Родитель.Родитель.Родитель.Родитель КАК Родитель4
ИЗ
    Справочник.Номенклатура КАК Спр
ГДЕ
    НЕ Спр.ЭтоГруппа

Далее при обходе результата запроса мы последовательно проверяем заполненность колонок. Если последний выбранный родитель (в примере — Родитель4) не пустой, вызывается рекурсивная процедура для довыборки оставшихся вышестоящих уровней. Однако такой подход усложняет код и при большом числе соединений создает лишнюю нагрузку на СУБД.

Способ 2. Оптимальная сборка пути в оперативной памяти (в один запрос)

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

Рассмотрим по шагам реализацию данного алгоритма:


// 1. Выбираем элементы и группы одним простым запросом
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Ссылка,
    |	Номенклатура.Наименование КАК Наименование,
    |	Номенклатура.Родитель КАК Родитель,
    |	Номенклатура.ЭтоГруппа КАК ЭтоГруппа
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура";

ТаблицаДанных = Запрос.Выполнить().Выгрузить();

// 2. Индексируем данные в оперативной памяти с помощью Соответствия
ИерархияЭлементов = Новый Соответствие;
Для Каждого Стр Из ТаблицаДанных Цикл
    ДанныеУзла = Новый Структура("Наименование, Родитель", Стр.Наименование, Стр.Родитель);
    ИерархияЭлементов.Вставить(Стр.Ссылка, ДанныеУзла);
КонецЦикла;

// 3. Формируем колонку полного наименования
ТаблицаДанных.Колонки.Добавить("ПолныйПуть", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(500)));

Для Каждого Стр Из ТаблицаДанных Цикл
    ПолныйПуть = Стр.Наименование;
    ТекущийРодитель = Стр.Родитель;
    
    // Поднимаемся вверх по дереву без обращений к базе данных
    Пока ЗначениеЗаполнено(ТекущийРодитель) Цикл
        УзелРодителя = ИерархияЭлементов[ТекущийРодитель];
        Если УзелРодителя = Неопределено Тогда
            Прервать;
        КонецЕсли;
        
        ПолныйПуть = УзелРодителя.Наименование + " / " + ПолныйПуть;
        ТекущийРодитель = УзелРодителя.Родитель;
    КонецЦикла;
    
    Стр.ПолныйПуть = ПолныйПуть;
КонецЦикла;

Выясним причину высокой эффективности этого метода: операции поиска по ключу в объекте Соответствие выполняются за время O(1). Обработка десятков тысяч записей в памяти занимает доли секунды и полностью избавляет от выполнения запросов в цикле.

Способ 3. Денормализация данных (хранение готового пути)

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

Разберем шаги реализации:

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

В этом случае при выгрузке данных нам достаточно просто выбрать реквизит ПолныйПуть стандартным запросом без каких-либо дополнительных вычислений и постобработки.

Архитектурная рекомендация для интеграции с OLAP и DWH

Проанализируем задачу со стороны целевой аналитической системы. Передача склеенной строки с разделителями (Группа / Подгруппа / Элемент) часто является антипаттерном в хранилищах данных, так как внешним алгоритмам приходится динамически разбивать строку на части для построения срезов.

Для интеграции с внешними BI- и OLAP-системами предпочтительнее передавать данные в одном из следующих форматов:

  1. Родительская связь (Parent-Child): таблица с полями Идентификатор, ИдентификаторРодителя и Наименование. Большинство OLAP-систем строят динамические иерархии на основе этой схемы автоматически.
  2. Фиксированные уровни: плоская таблица с отдельными колонками Уровень1, Уровень2, Уровень3 и так далее, сформированная на стороне 1С в процессе постобработки.
← На главную