Рассмотрим распространенную задачу разработки в 1С: у нас есть справочник (например, Номенклатура), который имеет свою стандартную метаданную иерархию. Однако бизнес-логика требует выстроить данные в виде дерева по другому, произвольному полю — например, по реквизиту ГлавныйРодитель, который также имеет тип ссылки на этот же справочник. Возникает вопрос: как получить полноценное многоуровневое дерево из запроса?
Проанализируем ситуацию и разберем по шагам все возможные способы решения данной задачи, так как стандартный язык запросов 1С имеет в этом плане определенные ограничения.
Сначала выясним причину, почему нельзя решить задачу "в лоб" одним простым запросом. Стандартный язык запросов 1С умеет автоматически разворачивать иерархию только для тех объектов метаданных, у которых включена платформенная иерархия. Для пользовательских реквизитов (таких как ГлавныйРодитель) встроенной функции рекурсивного разворота нет.
Часто разработчики пытаются использовать оператор итогов:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Элемент,
Номенклатура.ГлавныйРодитель КАК ГлавныйРодитель
ИЗ
Справочник.Номенклатура КАК Номенклатура
ИТОГИ ПО
ГлавныйРодитель
Важное ограничение: этот подход не выстроит полноценную многоуровневую иерархию, если глубина вложенности заранее неизвестна. Конструкция ИТОГИ ПО создаст лишь плоскую группировку (один уровень вложенности: родитель — и сразу под ним подчиненные элементы). Для реального многоуровневого дерева неизвестной глубины чистый запрос без дополнительных механизмов не сработает.
Посмотрим на самый элегантный и рекомендуемый способ визуализации таких данных. СКД отлично умеет строить произвольные иерархии без написания сложного программного кода.
Разберем по шагам, как это настроить:
ГлавныйРодитель.Номенклатура (или ссылку на элемент).ГлавныйРодитель.При такой настройке платформа 1С сама сгенерирует скрытые пакетные запросы и рекурсивно выстроит многоуровневое дерево для вывода.
Если нам нужно получить иерархическое дерево именно в табличном виде на сервере для дальнейшей сложной программной обработки (а не просто вывести в отчет), мы можем использовать метод транзитивного замыкания.
Этот метод позволяет обойти ограничение языка запросов 1С, в котором нет встроенных рекурсивных CTE-выражений (Common Table Expressions). Суть метода заключается в динамическом формировании пакетного запроса, который в цикле многократно соединяет таблицу саму с собой.
Рассмотрим, что дает этот подход:
Для реализации потребуется написать программный код, который в цикле (например, ПОКА Истина ЦИКЛ) будет выполнять запрос с ЛЕВОЕ СОЕДИНЕНИЕ предыдущего результата с исходной таблицей, пока количество строк перестанет увеличиваться. Для автоматического построения подобных конструкций можно использовать инструмент для формирования текста запроса родителей всех уровней иерархии.
Если выгружать сложные запросы не хочется, всегда остается вариант получить плоскую таблицу с помощью обычного запроса и выстроить иерархию программно, используя объект ДеревоЗначений.
Мы выгружаем данные в плоскую Таблицу Значений, а затем с помощью перебора строк (или использования метода НайтиСтроки) рекурсивно переносим их в структуру ДеревоЗначений. Для практической реализации такого алгоритма подойдёт обработка для вывода результата запроса в дерево значений.
Важный нюанс при связке с СКД:
Если мы решили обойти данные в коде, сформировали красивое объектное ДеревоЗначений, а затем хотим вывести его через СКД (передав как внешний набор данных), мы столкнемся с проблемой — структура дерева в СКД теряется и становится плоской.
Чтобы иерархия сохранилась, при заполнении дерева в коде необходимо вручную создать служебные колонки целочисленного типа. Назовем их, например, ИдентификаторСтроки и ИдентификаторРодителя. Мы пронумеровываем каждую строку уникальным числом, а в колонку родителя записываем идентификатор вышестоящей строки. Передав такую плоскую таблицу с идентификаторами во внешний набор данных СКД, мы снова идем на вкладку Связи наборов данных и настраиваем связь источника (ИдентификаторСтроки) и приемника (ИдентификаторРодителя). Только так СКД поймет программно сформированную структуру.
Рассмотрев все варианты, можно сделать вывод: если вам нужен просто отчет — используйте связи наборов данных в СКД. Если вам нужно обрабатывать данные программно и структура дерева не очень глубокая — формируйте ДеревоЗначений с помощью кода. А для тяжелых аналитических выборок со сложными связями и проверками на "бесконечные циклы" отлично подойдет алгоритм транзитивного замыкания на языке запросов.