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

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

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

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

Способ 1. Обращение через точку (Разыменование полей)

Самый простой и очевидный способ, который первым приходит в голову — это использование разыменования ссылочных полей, то есть обращение «через точку». Многие разработчики слышали, что множественные обращения через точку — это плохая практика («антипаттерн»), однако важно понимать, почему это так и когда этим правилом можно пренебречь.

Когда вы пишете в запросе конструкцию вида Спр.Родитель.Наименование, платформа 1С при выполнении запроса к СУБД (SQL Server, PostgreSQL) преобразует это в левое соединение (LEFT JOIN) с таблицей этого же справочника.

Если нам нужно получить родителей до 5-го уровня, запрос будет выглядеть следующим образом:


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

Преимущества:

  1. Код очень легко писать и читать.
  2. Для небольшой глубины иерархии (3-5 уровней) современные планировщики SQL-запросов отлично справляются с такой конструкцией.

Недостатки и ограничения:

  1. Жесткая привязка к структуре. Если уровень вложенности изменится (например, станет 10), код придется переписывать.
  2. Ограничения СУБД. Существует лимит на количество таблиц в одном запросе (обычно 256). Если вы запросите 10 реквизитов для каждого из 10 уровней родителей, вы можете упереться в этот лимит или получить очень медленный план запроса.

Тем не менее, если у вас фиксированная иерархия (например, не более 5-7 уровней), использование явного обращения к полям родителей является вполне рабочим решением.

Способ 2. Использование явных соединений

Чтобы понять, как работает первый способ «под капотом», или если вам нужно получить более сложные данные (например, условия соединения зависят от реквизитов), можно написать запрос с явными соединениями. Это то, во что платформа превращает обращение через точку.

Рассмотрим, как это выглядит:


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

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

Способ 3. Итоги по иерархии и программная обработка

Если глубина иерархии неизвестна или может меняться динамически, жестко прописывать Родитель.Родитель... нельзя. В этом случае нам поможет механизм итогов запроса и оператор ИЕРАРХИЯ.

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

1. Мы пишем запрос, который выбирает нужные нам элементы.

2. Добавляем конструкцию ИТОГИ ПО ... ИЕРАРХИЯ.


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Ссылка,
    Номенклатура.ДопРеквизит КАК ДопРеквизит
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Ссылка В(&СписокИнтересующихЭлементов)

ИТОГИ ПО
    Ссылка ИЕРАРХИЯ

При обходе результата такого запроса платформа 1С автоматически «подтянет» всех родителей для элементов, попавших в выборку, даже если мы явно не указывали их в условии ГДЕ. Важно понимать, что в выборку попадут как сами элементы, так и все их вышестоящие группы.

Как это обработать:

Можно использовать двойной проход или выгрузку в Таблицу Значений. Один из эффективных вариантов был предложен в обсуждении:

  1. Выполняем запрос с итогами по иерархии.
  2. Обходим результат, собирая все найденные ссылки (и элементы, и их родителей) в один массив или временную таблицу.
  3. Делаем второй запрос (или соединение внутри пакета), где уже к этому полному списку присоединяем нужные данные.

Способ 4. Оптимизация на уровне платформы (CTE)

Важно отметить изменение, которое произошло в платформе 1С начиная с версий 8.3.14. Разработчики оптимизировали работу оператора В ИЕРАРХИИ для Microsoft SQL Server.

Теперь, когда вы используете конструкцию:


ГДЕ Ссылка В ИЕРАРХИИ (&МассивЭлементов)

Платформа может использовать механизм CTE (Common Table Expressions) — обобщенные табличные выражения. Это позволяет выполнять рекурсивную выборку на уровне SQL-сервера значительно быстрее, чем старыми методами (через создание временных таблиц с множественными вставками).

Это означает, что для получения всей ветки (родителей или потомков) теперь не обязательно писать сложные циклы — платформа умеет делать это эффективно сама. Однако этот метод возвращает плоский список, и вам все равно придется программно разбирать, кто кому является родителем, если нужна структура дерева.

Способ 5. Архитектурный подход (Регистр сведений с иерархией)

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

Речь идет о реализации паттерна «Транзитивное замыкание».

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

Структура регистра:

Принцип работы:

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

Пример данных в регистре:

Если у нас есть структура: Группа1 -> Группа2 -> ТоварА.

В регистре будут записи:

Как это использовать в запросе:

Теперь, чтобы получить всех родителей товара или найти все товары, входящие в группу любого уровня, нам не нужны рекурсии, ИЕРАРХИЯ или множественные точки. Достаточно простого внутреннего соединения:


ВЫБРАТЬ
    РегистрИерархии.Родитель КАК ЛюбойРодитель
ИЗ
    РегистрСведений.ИерархияНоменклатуры КАК РегистрИерархии
ГДЕ
    РегистрИерархии.Партнер = &Товар

Плюсы: Максимальная скорость чтения данных. Запрос выполняется мгновенно.

Минусы: Замедление записи элементов справочника (необходимо обновлять регистр) и увеличение объема базы данных.

Заключение

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

  1. Если иерархия небольшая и фиксированная (до 5 уровней) — используйте обращение через точку (Спр.Родитель.Родитель). Это самый читаемый вариант.
  2. Если глубина неизвестна, но нужно получить данные в отчете или дереве — используйте ИТОГИ ПО ... ИЕРАРХИЯ.
  3. Если требуется максимальная производительность при чтении и вы готовы пожертвовать скоростью записи — реализуйте регистр сведений для хранения полной иерархии.
← На главную