Разработка и Конфигурация · 10 мин
1С: как запросом получить всех родителей элемента
Как в запросе 1С получить всех родителей элемента иерархического справочника: оператор В ИЕРАРХИИ, итоги по иерархии, обход в цикле. Примеры кода и типичные ошибки.
Получить всех родителей элемента иерархического справочника — типовая задача в 1С: например, нужно определить полный путь номенклатуры по группам, проверить принадлежность подразделения к верхнеуровневому филиалу или вывести цепочку подчинённости. Проблема в том, что язык запросов 1С не поддерживает рекурсивные соединения напрямую, поэтому «всех родителей одним запросом» получить в чистом виде нельзя — нужны обходные приёмы.
Существует три рабочих подхода: обращение к полю Родитель с заданным числом уровней через точку, итоги по иерархии с оператором В ИЕРАРХИИ и программный обход родителей в цикле с отдельными запросами. Выбор зависит от того, известна ли глубина иерархии заранее и какой результат нужен — сам список родителей или агрегированные итоги.
Ниже разобраны все три способа с готовыми примерами кода, ограничения каждого подхода, способы проверить результат и типичные ошибки, из-за которых запрос возвращает пустую выборку или лишние строки.
Как устроена иерархия и почему запрос «в лоб» не работает
В иерархических справочниках 1С каждый элемент хранит ссылку на родителя в стандартном реквизите Родитель. У элемента верхнего уровня родитель пустой — это ПустаяСсылка соответствующего типа. Таким образом, полный путь от элемента до корня — это цепочка переходов по ссылке, длина которой заранее неизвестна.
Язык запросов 1С не поддерживает рекурсивные конструкции вроде WITH RECURSIVE, которые есть в «чистом» SQL. Соединить таблицу саму с собой можно, но только на фиксированное число уровней, записанное в тексте запроса явно. Поэтому универсального текста запроса «дай мне всех предков» не существует — всегда есть компромисс между простотой запроса и глубиной обхода.
Способ 1: получить родителей через точку при известной глубине
Если максимальная глубина иерархии в вашем справочнике известна и невелика (обычно 3–5 уровней), самый быстрый вариант — обратиться к родителю через точку столько раз, сколько нужно уровней. Это делается прямо в тексте запроса без соединений.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Элемент,
| Номенклатура.Родитель КАК Родитель1,
| Номенклатура.Родитель.Родитель КАК Родитель2,
| Номенклатура.Родитель.Родитель.Родитель КАК Родитель3
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ТекущийЭлемент);
Результат = Запрос.Выполнить().Выбрать();
Результат.Следующий();
Каждое поле РодительN вернёт ссылку на предка соответствующего уровня или NULL, если уровней меньше. Платформа 1С безопасно обрабатывает разыменование через пустую ссылку — ошибки не будет, просто значение окажется пустым.
Ограничение очевидно: если в данных появится уровень глубже, чем предусмотрено в запросе, его родители «обрежутся» молча. Поэтому способ применим только там, где глубина иерархии контролируется прикладной логикой — например, в справочнике видов номенклатуры с фиксированными тремя уровнями.
Способ 2: оператор В ИЕРАРХИИ и итоги по иерархии
Оператор В ИЕРАРХИИ и конструкция ИТОГИ ПО ... ТОЛЬКО ИЕРАРХИЯ работают в обратном направлении: они позволяют получить всех потомков заданного родителя или сгруппировать данные с учётом дерева. Но с их помощью можно решить и обратную задачу — проверить, входит ли элемент в цепочку подчинённости конкретной группы.
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Элемент,
| Номенклатура.Родитель КАК Родитель
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В ИЕРАРХИИ(&Группа)
|ИТОГИ ПО
| Номенклатура.Ссылка ТОЛЬКО ИЕРАРХИЯ";
При обходе результата с типом обхода ПоГруппировкамСИерархией вы получите дерево, где видна вся структура вложенности. Это удобно, когда конечная цель — не список предков одного элемента, а отчёт по дереву: остатки по группам, обороты по подразделениям с накоплением вверх по иерархии.
Выборка = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Пока Выборка.Следующий() Цикл
// Выборка.Элемент — группа или элемент с учётом иерархии
КонецЦикла;Условие В ИЕРАРХИИ(&Группа) включает в выборку и саму группу-параметр. Если сама группа не нужна, добавьте условие И Номенклатура.Ссылка <> &Группа либо отфильтруйте её при обходе.
Способ 3: обход всех родителей в цикле
Когда глубина иерархии неизвестна и нужен именно полный список предков, используют программный обход: на каждой итерации запрашивают родителя текущего элемента и поднимаются выше, пока не встретится пустая ссылка. Это единственный способ, гарантирующий результат при произвольной глубине.
Функция ПолучитьВсехРодителей(Элемент)
СписокРодителей = Новый Массив;
ТекущийРодитель = Элемент.Родитель;
Пока ЗначениеЗаполнено(ТекущийРодитель) Цикл
СписокРодителей.Добавить(ТекущийРодитель);
ТекущийРодитель = ТекущийРодитель.Родитель;
КонецЦикла;
Возврат СписокРодителей;
КонецФункции
Обращение Элемент.Родитель по ссылке — это на самом деле скрытый запрос к базе на каждом шаге. Для разовой проверки это приемлемо, но в массовой обработке (например, при расчёте полного пути для тысяч элементов) такие обращения в цикле дадут тысячи отдельных запросов и заметно просадят производительность.
Оптимизация для массовой обработки: одним запросом выгрузить весь справочник с колонками «Ссылка» и «Родитель» в соответствие или таблицу значений, а затем строить цепочки родителей уже в памяти, без обращений к базе. Это меняет сложность с «N запросов на каждый элемент» на один запрос и быстрые поиски в соответствии.
- 1Выполните один запрос: ВЫБРАТЬ Ссылка, Родитель ИЗ Справочник.Номенклатура
- 2Выгрузите результат и постройте соответствие: ключ — ссылка, значение — родитель
- 3Для каждого элемента идите по цепочке родителей через соответствие, пока родитель не станет пустым
- 4Соберите найденных родителей в массив или строку пути
- 5Проверьте результат на нескольких элементах разной глубины
Как выбрать подходящий способ
Выбор зависит от трёх факторов: известна ли глубина иерархии, сколько элементов нужно обработать и какой результат требуется — список ссылок, строка пути или агрегированные итоги.
Как выбрать действие
Для отчётов на СКД предпочтителен вариант с итогами по иерархии: он выполняется на стороне СУБД и не требует кода. Для прикладной логики в модулях чаще удобнее цикл с соответствием — он прозрачнее и легче отлаживается.
Как проверить результат и избежать ошибок
После реализации проверьте цепочку родителей на контрольных примерах: элементе верхнего уровня (должен вернуть пустой список), элементе среднего уровня и самом глубоком элементе справочника. Сверьте полученный путь с тем, что видно в дереве справочника в режиме предприятия.
Как проверить результат
Список родителей пуст
Возвращается пустая ссылка как элемент списка
Цепочка совпадает с деревом в интерфейсе
Цепочка обрывается раньше корня
Обработка тысяч элементов занимает секунды
Каждый элемент обрабатывается ощутимо долго — запросы в цикле
В выборку входят потомки и сама группа
Группа-параметр неожиданно отсутствует или лишняя в отчёте
Отдельного внимания заслуживает целостность данных: если в справочнике по ошибке образовалось зацикливание (элемент ссылается сам на себя через цепочку родителей), цикл обхода не завершится. В отлаженной базе это редкость — платформа контролирует иерархию при записи, — но при загрузке данных из внешних источников стоит добавить ограничение на число итераций, например не более 100.
FAQ
Можно ли одним запросом получить всех родителей без ограничения глубины?
Нет, язык запросов 1С не поддерживает рекурсию. Варианты обхода: фиксированное число уровней через Родитель.Родитель..., либо программный обход в цикле, либо выгрузка пар Ссылка–Родитель и построение цепочек в памяти.
Чем В ИЕРАРХИИ отличается от простого условия Родитель = &Группа?
Условие Родитель = &Группа вернёт только непосредственных потомков первого уровня. В ИЕРАРХИИ вернёт всех потомков на любой глубине, включая саму группу-параметр.
Как получить строку полного пути элемента, например «Каталог/Группа/Подгруппа»?
Соберите массив родителей обходом в цикле, затем разверните его и соедините наименования через разделитель. Для массовой обработки стройте пути из предварительно выгруженного соответствия ссылок и наименований.
Почему Родитель.Родитель возвращает пустое значение, хотя группа есть?
Проверьте, что элемент действительно находится на третьем уровне или глубже. Также убедитесь, что справочник имеет вид иерархии, допускающий нужную структуру: в справочниках с иерархией «группы и элементы» элемент не может быть родителем другого элемента.
Что быстрее: запрос с разыменованием или цикл?
Для одного элемента разницы практически нет. Для массовой обработки быстрее всего один запрос с выгрузкой пар Ссылка–Родитель и построение цепочек в памяти. Медленнее всего — обращение к реквизитам через ссылку в цикле по каждому элементу.
Что важно запомнить
Рекурсивного запроса в 1С нет: родителей получают разыменованием при известной глубине, итогами по иерархии для агрегации или обходом в цикле при произвольной глубине. Для массовых операций выгружайте пары Ссылка–Родитель одним запросом и стройте цепочки в памяти, а цикл всегда защищайте ограничителем итераций.