Работа с иерархическими структурами данных в 1С — это классическая задача, с которой сталкивается каждый разработчик. Одной из наиболее частых подзадач является поиск «корня» дерева — той самой группы верхнего уровня, в которую вложен конкретный товар или услуга. Сложность заключается в том, что язык запросов 1С не поддерживает рекурсию в чистом виде, а глубина вложенности в справочниках (например, в справочнике Номенклатура) может быть произвольной. В этой статье мы подробно разберем все доступные методы решения этой задачи: от простых и быстрых до архитектурно правильных и производительных.
Рассмотрим наиболее элегантный встроенный механизм платформы 1С — получение итогов по иерархии. Этот метод позволяет платформе самостоятельно вычислить всю цепочку родителей для выбранного элемента. Проанализируем, как это работает на практике.
Когда мы используем предложение ИТОГИ ПО Ссылка ТОЛЬКО ИЕРАРХИЯ, система при обходе результата запроса предоставляет доступ ко всем вышестоящим узлам. Разберем по шагам реализацию этого метода в коде:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка = &Ссылка
|ИТОГИ ПО
| Ссылка ТОЛЬКО ИЕРАРХИЯ";
Запрос.УстановитьПараметр("Ссылка", ВыбраннаяНоменклатура);
Результат = Запрос.Выполнить();
// Используем обход по группировкам с иерархией
Выборка = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Если Выборка.Следующий() Тогда
// Первая запись в такой выборке — это всегда корень (верхний уровень)
ГруппаВерхнегоУровня = Выборка.Ссылка;
Сообщить("Корневая группа: " + ГруппаВерхнегоУровня);
КонецЕсли;
Важный нюанс: Этот метод отлично подходит для получения корня одного конкретного элемента. Однако стоит помнить, что использование ИТОГИ создает дополнительную нагрузку на сервер базы данных, так как платформа выполняет сложные соединения для построения дерева. Если вам нужно получить корни для тысячи элементов одновременно, этот подход может замедлить работу системы.
Посмотрим на более «прямолинейный» способ, который часто применяют, когда глубина вложенности справочника заранее известна или ограничена правилами ведения учета (например, не более 5-7 уровней). Мы можем использовать вложенные обращения через поле Родитель. Для автоматизации подготовки таких конструкций подойдёт инструмент для интерактивного формирования текста запроса к родителям справочника.
Проанализируем пример запроса, который последовательно проверяет каждого родителя на «пустоту». Если родитель родителя является пустой ссылкой, значит, текущий родитель и есть корень. Рассмотрим структуру такого запроса:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Товар,
ВЫБОР
КОГДА Номенклатура.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
ТОГДА Номенклатура.Ссылка
КОГДА Номенклатура.Родитель.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
ТОГДА Номенклатура.Родитель
КОГДА Номенклатура.Родитель.Родитель.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
ТОГДА Номенклатура.Родитель.Родитель
КОГДА Номенклатура.Родитель.Родитель.Родитель.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
ТОГДА Номенклатура.Родитель.Родитель.Родитель
ИНАЧЕ Номенклатура.Родитель.Родитель.Родитель.Родитель
КОНЕЦ КАК КорневаяГруппа
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ЭтоГруппа = ЛОЖЬ
Разберем недостатки этого метода. Во-первых, он жестко ограничен количеством уровней, прописанных в коде. Если пользователь создаст восьмой уровень вложенности, запрос вернет некорректные данные. Во-вторых, каждое обращение через точку (Родитель.Родитель) в языке запросов превращается в отдельное левое соединение (LEFT JOIN) на уровне SQL-запроса. Если у вас 7 уровней проверки, то для каждой строки справочника СУБД будет выполнять 7 соединений, что крайне негативно сказывается на производительности при больших объемах данных.
Если нам необходимо гарантированно найти корень при любой глубине вложенности и сделать это эффективно для списка элементов, мы можем применить итерационный подход в коде 1С с использованием временных таблиц. Выясним, как реализовать этот алгоритм.
Ссылка (целевой элемент) и ТекущийРодитель (изначально равен родителю элемента).ТекущийРодитель на его собственного родителя, пока в таблице остаются записи, у которых родитель не пуст.Рассмотрим схематичный пример логики:
// 1. Помещаем данные во временную таблицу Менеджера Временных Таблиц
// 2. Запускаем цикл
ЦиклДостиженияКорня = Истина;
Пока ЦиклДостиженияКорня Цикл
// Запрос: Обновить ВТ, где Родитель = (Выбрать Родителя из Справочника по ТекущемуРодителю)
// Если после выполнения запроса количество измененных строк = 0 (все родители стали пустыми),
// прерываем цикл.
КонецЦикла;
Этот метод является наиболее надежным с точки зрения архитектуры, так как он не зависит от настроек справочника и работает с неограниченной вложенностью. Похожий принцип рекурсивного обхода всех уровней иерархии применяется и для того, чтобы выборочно удалить данные организации с автоматическим поиском всех связанных ссылок.
Проанализируем ситуацию, когда справочник Номенклатура содержит сотни тысяч записей, а отчеты, требующие группировки по «Головной папке», строятся ежедневно. В этом случае лучшим решением будет денормализация данных.
Суть метода заключается в создании у справочника дополнительного реквизита — ГоловнаяГруппа с типом СправочникСсылка.Номенклатура. Рассмотрим алгоритм внедрения:
ГоловнаяГруппа в метаданные.ПередЗаписью прописываем логику: если это новый элемент или изменился родитель, программно вычисляем корень (поднимаясь вверх по дереву) и записываем его в этот реквизит.Теперь в любом запросе получение группы верхнего уровня превращается в обычное чтение поля: Номенклатура.ГоловнаяГруппа. Это работает мгновенно, не требует соединений и итогов, что является идеальным решением для высоконагруженных систем.
Если ваша задача — просто вывести группу верхнего уровня в отчете, то использование СКД — самый быстрый путь. Система компоновки данных умеет работать с иерархией «из коробки».
В настройках набора данных достаточно указать связь поля Ссылка с полем Родитель. После этого в настройках отчета можно добавить группировку по иерархии. Если же нужно именно отдельное поле с корнем, в СКД можно использовать вычисляемые поля с функциями агрегации или специализированные настройки ролей полей, что позволит платформе самой вычислить нужные значения без написания сложных запросов.
Подведем итоги нашего анализа. Выбор метода зависит от контекста задачи:
ИТОГИ ПО... ИЕРАРХИЯ (Метод 1).ВЫБОР КОГДА на 3-5 уровней (Метод 2).ГоловнаяГруппа (Метод 4).Помните, что избыточные обращения через точку в запросах (Ссылка.Родитель.Родитель...) — это одна из главных причин деградации производительности системы. Стремитесь к тому, чтобы ваши запросы были плоскими, а сложные иерархические вычисления выполнялись либо один раз при записи, либо оптимизированными порциями.