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

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

Рассмотрим распространенную задачу разработки в 1С: у нас есть справочник (например, Номенклатура), который имеет свою стандартную метаданную иерархию. Однако бизнес-логика требует выстроить данные в виде дерева по другому, произвольному полю — например, по реквизиту ГлавныйРодитель, который также имеет тип ссылки на этот же справочник. Возникает вопрос: как получить полноценное многоуровневое дерево из запроса?

Проанализируем ситуацию и разберем по шагам все возможные способы решения данной задачи, так как стандартный язык запросов 1С имеет в этом плане определенные ограничения.

Ограничения стандартного запроса и оператора ИТОГИ ПО

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

Часто разработчики пытаются использовать оператор итогов:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Элемент,
    Номенклатура.ГлавныйРодитель КАК ГлавныйРодитель
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ИТОГИ ПО
    ГлавныйРодитель

Важное ограничение: этот подход не выстроит полноценную многоуровневую иерархию, если глубина вложенности заранее неизвестна. Конструкция ИТОГИ ПО создаст лишь плоскую группировку (один уровень вложенности: родитель — и сразу под ним подчиненные элементы). Для реального многоуровневого дерева неизвестной глубины чистый запрос без дополнительных механизмов не сработает.

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

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

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

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

При такой настройке платформа 1С сама сгенерирует скрытые пакетные запросы и рекурсивно выстроит многоуровневое дерево для вывода.

Решение 2: Математический подход — Транзитивное замыкание

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

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

Рассмотрим, что дает этот подход:

  1. Мы формируем промежуточную таблицу, которая содержит абсолютно все связи между предками и потомками на любую глубину вложенности.
  2. Мы можем вычислить точный уровень вложенности каждого элемента.
  3. Мы можем найти корень дерева для каждого конкретного элемента.
  4. Защита от зацикливания: при построении кастомных иерархий пользователи часто могут по ошибке указать элементам друг друга в качестве родителей (Элемент А ссылается на Б, а Б на А). Транзитивное замыкание позволяет отследить и исключить такие циклические ссылки, которые иначе привели бы к зависанию сервера (бесконечной рекурсии).

Для реализации потребуется написать программный код, который в цикле (например, ПОКА Истина ЦИКЛ) будет выполнять запрос с ЛЕВОЕ СОЕДИНЕНИЕ предыдущего результата с исходной таблицей, пока количество строк перестанет увеличиваться. Для автоматического построения подобных конструкций можно использовать инструмент для формирования текста запроса родителей всех уровней иерархии.

Решение 3: Обход выборки в коде и формирование Дерева Значений

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

Мы выгружаем данные в плоскую Таблицу Значений, а затем с помощью перебора строк (или использования метода НайтиСтроки) рекурсивно переносим их в структуру ДеревоЗначений. Для практической реализации такого алгоритма подойдёт обработка для вывода результата запроса в дерево значений.

Важный нюанс при связке с СКД:
Если мы решили обойти данные в коде, сформировали красивое объектное ДеревоЗначений, а затем хотим вывести его через СКД (передав как внешний набор данных), мы столкнемся с проблемой — структура дерева в СКД теряется и становится плоской.

Чтобы иерархия сохранилась, при заполнении дерева в коде необходимо вручную создать служебные колонки целочисленного типа. Назовем их, например, ИдентификаторСтроки и ИдентификаторРодителя. Мы пронумеровываем каждую строку уникальным числом, а в колонку родителя записываем идентификатор вышестоящей строки. Передав такую плоскую таблицу с идентификаторами во внешний набор данных СКД, мы снова идем на вкладку Связи наборов данных и настраиваем связь источника (ИдентификаторСтроки) и приемника (ИдентификаторРодителя). Только так СКД поймет программно сформированную структуру.

Резюме

Рассмотрев все варианты, можно сделать вывод: если вам нужен просто отчет — используйте связи наборов данных в СКД. Если вам нужно обрабатывать данные программно и структура дерева не очень глубокая — формируйте ДеревоЗначений с помощью кода. А для тяжелых аналитических выборок со сложными связями и проверками на "бесконечные циклы" отлично подойдет алгоритм транзитивного замыкания на языке запросов.

← На главную