К статье
Практический материал

Разработка и Конфигурация · 10 мин

1С: как запросом получить всех родителей элемента

Как в запросе 1С получить всех родителей элемента иерархического справочника: оператор В ИЕРАРХИИ, итоги по иерархии, обход в цикле. Примеры кода и типичные ошибки.

Получить всех родителей элемента иерархического справочника — типовая задача в 1С: например, нужно определить полный путь номенклатуры по группам, проверить принадлежность подразделения к верхнеуровневому филиалу или вывести цепочку подчинённости. Проблема в том, что язык запросов 1С не поддерживает рекурсивные соединения напрямую, поэтому «всех родителей одним запросом» получить в чистом виде нельзя — нужны обходные приёмы.

Существует три рабочих подхода: обращение к полю Родитель с заданным числом уровней через точку, итоги по иерархии с оператором В ИЕРАРХИИ и программный обход родителей в цикле с отдельными запросами. Выбор зависит от того, известна ли глубина иерархии заранее и какой результат нужен — сам список родителей или агрегированные итоги.

Ниже разобраны все три способа с готовыми примерами кода, ограничения каждого подхода, способы проверить результат и типичные ошибки, из-за которых запрос возвращает пустую выборку или лишние строки.

Как устроена иерархия и почему запрос «в лоб» не работает

В иерархических справочниках 1С каждый элемент хранит ссылку на родителя в стандартном реквизите Родитель. У элемента верхнего уровня родитель пустой — это ПустаяСсылка соответствующего типа. Таким образом, полный путь от элемента до корня — это цепочка переходов по ссылке, длина которой заранее неизвестна.

Язык запросов 1С не поддерживает рекурсивные конструкции вроде WITH RECURSIVE, которые есть в «чистом» SQL. Соединить таблицу саму с собой можно, но только на фиксированное число уровней, записанное в тексте запроса явно. Поэтому универсального текста запроса «дай мне всех предков» не существует — всегда есть компромисс между простотой запроса и глубиной обхода.

Способ 1: получить родителей через точку при известной глубине

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

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Номенклатура.Ссылка КАК Элемент,

| Номенклатура.Родитель КАК Родитель1,

| Номенклатура.Родитель.Родитель КАК Родитель2,

| Номенклатура.Родитель.Родитель.Родитель КАК Родитель3

|ИЗ

| Справочник.Номенклатура КАК Номенклатура

|ГДЕ

| Номенклатура.Ссылка = &Ссылка";

Запрос.УстановитьПараметр("Ссылка", ТекущийЭлемент);

Результат = Запрос.Выполнить().Выбрать();

Результат.Следующий();

Каждое поле РодительN вернёт ссылку на предка соответствующего уровня или NULL, если уровней меньше. Платформа 1С безопасно обрабатывает разыменование через пустую ссылку — ошибки не будет, просто значение окажется пустым.

Ограничение очевидно: если в данных появится уровень глубже, чем предусмотрено в запросе, его родители «обрежутся» молча. Поэтому способ применим только там, где глубина иерархии контролируется прикладной логикой — например, в справочнике видов номенклатуры с фиксированными тремя уровнями.

Способ 2: оператор В ИЕРАРХИИ и итоги по иерархии

Оператор В ИЕРАРХИИ и конструкция ИТОГИ ПО ... ТОЛЬКО ИЕРАРХИЯ работают в обратном направлении: они позволяют получить всех потомков заданного родителя или сгруппировать данные с учётом дерева. Но с их помощью можно решить и обратную задачу — проверить, входит ли элемент в цепочку подчинённости конкретной группы.

Запрос.Текст =

"ВЫБРАТЬ

| Номенклатура.Ссылка КАК Элемент,

| Номенклатура.Родитель КАК Родитель

|ИЗ

| Справочник.Номенклатура КАК Номенклатура

|ГДЕ

| Номенклатура.Ссылка В ИЕРАРХИИ(&Группа)

|ИТОГИ ПО

| Номенклатура.Ссылка ТОЛЬКО ИЕРАРХИЯ";

При обходе результата с типом обхода ПоГруппировкамСИерархией вы получите дерево, где видна вся структура вложенности. Это удобно, когда конечная цель — не список предков одного элемента, а отчёт по дереву: остатки по группам, обороты по подразделениям с накоплением вверх по иерархии.

1c
Выборка = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Пока Выборка.Следующий() Цикл
	// Выборка.Элемент — группа или элемент с учётом иерархии
КонецЦикла;

Условие В ИЕРАРХИИ(&Группа) включает в выборку и саму группу-параметр. Если сама группа не нужна, добавьте условие И Номенклатура.Ссылка <> &Группа либо отфильтруйте её при обходе.

Способ 3: обход всех родителей в цикле

Когда глубина иерархии неизвестна и нужен именно полный список предков, используют программный обход: на каждой итерации запрашивают родителя текущего элемента и поднимаются выше, пока не встретится пустая ссылка. Это единственный способ, гарантирующий результат при произвольной глубине.

Функция ПолучитьВсехРодителей(Элемент)

СписокРодителей = Новый Массив;

ТекущийРодитель = Элемент.Родитель;

Пока ЗначениеЗаполнено(ТекущийРодитель) Цикл

СписокРодителей.Добавить(ТекущийРодитель);

ТекущийРодитель = ТекущийРодитель.Родитель;

КонецЦикла;

Возврат СписокРодителей;

КонецФункции

Обращение Элемент.Родитель по ссылке — это на самом деле скрытый запрос к базе на каждом шаге. Для разовой проверки это приемлемо, но в массовой обработке (например, при расчёте полного пути для тысяч элементов) такие обращения в цикле дадут тысячи отдельных запросов и заметно просадят производительность.

Оптимизация для массовой обработки: одним запросом выгрузить весь справочник с колонками «Ссылка» и «Родитель» в соответствие или таблицу значений, а затем строить цепочки родителей уже в памяти, без обращений к базе. Это меняет сложность с «N запросов на каждый элемент» на один запрос и быстрые поиски в соответствии.

Порядок действий для массового обхода
  1. 1
    Выполните один запрос: ВЫБРАТЬ Ссылка, Родитель ИЗ Справочник.Номенклатура
  2. 2
    Выгрузите результат и постройте соответствие: ключ — ссылка, значение — родитель
  3. 3
    Для каждого элемента идите по цепочке родителей через соответствие, пока родитель не станет пустым
  4. 4
    Соберите найденных родителей в массив или строку пути
  5. 5
    Проверьте результат на нескольких элементах разной глубины

Как выбрать подходящий способ

Выбор зависит от трёх факторов: известна ли глубина иерархии, сколько элементов нужно обработать и какой результат требуется — список ссылок, строка пути или агрегированные итоги.

Маршрут решения

Как выбрать действие

Глубина иерархии фиксирована и не превышает 3–5 уровнейИспользуйте разыменование Родитель.Родитель... прямо в запросе
Нужны итоги с накоплением по дереву (остатки, обороты по группам)Используйте В ИЕРАРХИИ и ИТОГИ ПО ... ТОЛЬКО ИЕРАРХИЯ
Глубина произвольная и элемент одинОбойдите родителей в цикле через Элемент.Родитель
Глубина произвольная и элементов тысячиВыгрузите Ссылка–Родитель одним запросом и стройте цепочки в памяти

Для отчётов на СКД предпочтителен вариант с итогами по иерархии: он выполняется на стороне СУБД и не требует кода. Для прикладной логики в модулях чаще удобнее цикл с соответствием — он прозрачнее и легче отлаживается.

Как проверить результат и избежать ошибок

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

Контрольные точки

Как проверить результат

Элемент верхнего уровня
Норма

Список родителей пуст

Тревожный признак

Возвращается пустая ссылка как элемент списка

Глубокий элемент
Норма

Цепочка совпадает с деревом в интерфейсе

Тревожный признак

Цепочка обрывается раньше корня

Скорость на массовой выборке
Норма

Обработка тысяч элементов занимает секунды

Тревожный признак

Каждый элемент обрабатывается ощутимо долго — запросы в цикле

Условие В ИЕРАРХИИ
Норма

В выборку входят потомки и сама группа

Тревожный признак

Группа-параметр неожиданно отсутствует или лишняя в отчёте

Типичные ошибки
01
Обращение к Родителю в цикле по ссылке для каждого из тысяч элементов — тысячи скрытых запросов и деградация производительности
02
Забытое условие ЗначениеЗаполнено в цикле — риск бесконечного цикла при порче данных или зацикливании иерархии
03
Ожидание, что В ИЕРАРХИИ вернёт родителей элемента — оператор работает в обратную сторону, от группы к потомкам
04
Жёсткая фиксация числа уровней в запросе без контроля — при росте дерева часть родителей молча теряется
05
Отсутствие фильтра ЭтоГруппа при смешанной иерархии — в выборку попадают элементы там, где ожидались только группы

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

FAQ

Можно ли одним запросом получить всех родителей без ограничения глубины?

Нет, язык запросов 1С не поддерживает рекурсию. Варианты обхода: фиксированное число уровней через Родитель.Родитель..., либо программный обход в цикле, либо выгрузка пар Ссылка–Родитель и построение цепочек в памяти.

Чем В ИЕРАРХИИ отличается от простого условия Родитель = &Группа?

Условие Родитель = &Группа вернёт только непосредственных потомков первого уровня. В ИЕРАРХИИ вернёт всех потомков на любой глубине, включая саму группу-параметр.

Как получить строку полного пути элемента, например «Каталог/Группа/Подгруппа»?

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

Почему Родитель.Родитель возвращает пустое значение, хотя группа есть?

Проверьте, что элемент действительно находится на третьем уровне или глубже. Также убедитесь, что справочник имеет вид иерархии, допускающий нужную структуру: в справочниках с иерархией «группы и элементы» элемент не может быть родителем другого элемента.

Что быстрее: запрос с разыменованием или цикл?

Для одного элемента разницы практически нет. Для массовой обработки быстрее всего один запрос с выгрузкой пар Ссылка–Родитель и построение цепочек в памяти. Медленнее всего — обращение к реквизитам через ссылку в цикле по каждому элементу.

Итог

Что важно запомнить

Рекурсивного запроса в 1С нет: родителей получают разыменованием при известной глубине, итогами по иерархии для агрегации или обходом в цикле при произвольной глубине. Для массовых операций выгружайте пары Ссылка–Родитель одним запросом и стройте цепочки в памяти, а цикл всегда защищайте ограничителем итераций.