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