Работа с иерархическими справочниками — одна из базовых задач разработчика на платформе 1С:Предприятие 8. Часто возникает необходимость не просто выбрать список элементов, а получить полное дерево иерархии, отфильтровать ветку, вывести элементы в порядке иерархии или построить плоскую таблицу с уровнями вложенности. В этой статье подробно разберем основные методы и конструкции языка запросов для решения этих задач.
Основная конструкция: В ИЕРАРХИИ
Ключевым инструментом отбора по иерархии в языке запросов 1С является оператор В ИЕРАРХИИ. Он позволяет отобрать не только сам указанный элемент (группу), но и все его подчиненные элементы на всех уровнях вложенности.
Синтаксис
ГДЕ Справочник.Ссылка В ИЕРАРХИИ(&Родитель) Пример: Получение всей подчиненной иерархии для конкретной группы
Допустим, нам нужно получить все номенклатуру, входящую в группу «Товары» и её подгруппы.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Родитель КАК Родитель,
| Номенклатура.ЭтоГруппа КАК ЭтоГруппа
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В ИЕРАРХИИ(&РодительГруппы)";
Запрос.УстановитьПараметр("РодительГруппу", Справочники.Номенклатура.НайтиПоНаименованию("Товары"));
Результат = Запрос.Выполнить.Выгрузить; Важно: Параметр в конструкции В ИЕРАРХИИ должен быть типа Ссылка на элемент справочника (группу). Если передать пустую ссылку (NULL), будут выбраны только корневые элементы (у которых Родитель = NULL).
Упорядочивание результата по иерархии (ИТОГИ ПО … ИЕРАРХИЯ)
Простой запрос с В ИЕРАРХИИ возвращает плоский набор строк. Чтобы результат был упорядочен так, чтобы группы шли перед своими элементами, а подгруппы, сразу после родительской группы, используется конструкция ИТОГИ ПО … ИЕРАРХИЯ (или АВТОУПОРЯДОЧИВАНИЕ).
Пример: Выгрузка дерева значений с сохранением иерархии
Это классический способ получить ДеревоЗначений, готовое к выводу на форму или в табличный документ.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления
|УПОРЯДОЧИТЬ ПО
| Номенклатура.ЭтоГруппа УБЫВ, // Группы идут первыми
| Номенклатура.Наименование
|ИТОГИ ПО
| Ссылка
|ИЕРАРХИЯ АВТОУПОРЯДОЧИВАНИЕ"; // Ключевое слово для построения дерева
РезультатЗапроса = Запрос.Выполнить;
// Выгрузка в ДеревоЗначений с соблюдением иерархии
ДеревоРезультат = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией); - УПОРЯДОЧИТЬ ПО Номенклатура.ЭтоГруппа УБЫВ — критически важно: группы должны идти перед элементами, иначе иерархия построится некорректно.
- ИТОГИ ПО Ссылка ИЕРАРХИЯ АВТОУПОРЯДОЧИВАНИЕ — указывает платформе автоматически определить уровни вложенности на основе реквизита Родитель в выборке и построить иерархическую структуру результата.
- ОбходРезультатаЗапроса.ПоГруппировкамСИерархией — режим выгрузки, преобразующий результат запроса в ДеревоЗначений.
Получение только верхнего уровня (корневых групп)
Иногда нужно получить только те элементы, у которых нет родителя (уровень 0).
Способ 1: Через условие Родитель = NULL
ВЫБРАТЬ Ссылка ИЗ Справочник.Номенклатура ГДЕ Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка) Способ 2: Через В ИЕРАРХИИ с пустой ссылкой
Конструкция В ИЕРАРХИИ(ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) также вернет только корневые элементы.
Функция ВернутьВерхнийУровеньИерархииНаСервере
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Справочник.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Справочник
|ГДЕ
| Справочник.Ссылка В ИЕРАРХИИ(ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка))";
Возврат Запрос.Выполнить;Выгрузить.ВыгрузитьКолонку("Ссылка");
КонецФункции Получение пути к элементу (Полное наименование / Иерархия родителей)
В языке запросов 1С нет рекурсивных CTE (Common Table Expressions), как в SQL. Поэтому получить полный путь к элементу (например, «Группа 1 / Подгруппа 2 / Товар») одним запросом «в лоб» нельзя. Используются два основных подхода:
Подход А: Временная таблица + программный обход (для старых платформ и сложных задач)
- Запросом выбираем все нужные элементы (ИД, Родитель, Наименование) во временную таблицу.
- В коде 1С строим карту (Соответствие) ИД -> Родитель.
- Для каждого элемента поднимаемся вверх по карте, собирая путь.
Подход Б: Использование объекта метаданных (Платформа 8.3.10+)
У объекта метаданных справочника есть метод ПолучитьИерархию, но он работает на уровне объектов, а не запроса. В запросе же можно использовать функцию ПРЕДСТАВЛЕНИЕ, если для справочника настроено полное наименование, но это работает только для вывода строки, а не для разбора по уровням.
Пример программного построения пути (после выборки в ТаблицуЗначений):
// Допустим, у нас ТаблицаЗначений с колонками: Ссылка, Родитель, Наименование
СоответствиеРодителей = Новый Соответствие;
Для Каждого Стр Из ТаблицаЗначений Цикл
СоответствиеРодителей.Вставить(Стр.Ссылка, Стр.Родитель);
КонецЦикла;
Для Каждого Стр Из ТаблицаЗначений Цикл
Путь = "";
ТекущийРодитель = Стр.Родитель;
Пока ТекущийРодитель <> Справочники.Номенклатура.ПустаяСсылка Цикл
СтрРодитель = ТаблицаЗначений.Найти(ТекущийРодитель, "Ссылка");
Если СтрРодитель = Неопределено Тогда Прервать; КонецЕсли;
Путь = СтрРодитель.Наименование + " / " + Путь;
ТекущийРодитель = СтрРодитель.Родитель;
КонецЦикла;
Стр.ПолныйПуть = Путь + Стр.Наименование;
КонецЦикла; Плоская таблица с динамическим количеством уровней (Раскрытие иерархии в колонки)
Задача: представить иерархию в виде таблицы, где колонки — это Уровень 1, Уровень 2, … Уровень N. Поскольку максимальная глубина заранее неизвестна, это делается в два прохода:
- Запросом определить максимальную глубину вложенности для отбираемой ветки.
- Сформировать текст запроса динамически, делая ЛЕВОЕ СОЕДИНЕНИЕ справочника с самим собой N раз (Self-Join).
Пример логики формирования такого запроса:
// 1. Определяем макс. глубину (упрощенно, через рекурсивную функцию на сервере или перебором)
// 2. Генерируем текст запроса:
/*
ВЫБРАТЬ
Ур1.Наименование КАК Уровень1,
Ур2.Наименование КАК Уровень2,
Ур3.Наименование КАК Уровень3,
Элемент.Наименование КАК Элемент
ИЗ
Справочник.Номенклатура КАК Ур1
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Ур2 ПО Ур2.Родитель = Ур1.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Ур3 ПО Ур3.Родитель = Ур2.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Элемент ПО Элемент.Родитель = Ур3.Ссылка
ГДЕ
Ур1.Родитель = &ПустаяСсылка
И (Ур3.Ссылка ЕСТЬ NULL ИЛИ Элемент.ЭтоГруппа = ЛОЖЬ) // Отбор только конечных элементов или групп нужного уровня
*/ Этот метод дает высокую производительность на больших объемах данных, так как вся работа выполняется СУБД.
Особенности иерархии элементов (не групп)
В 1С существует два вида иерархии:
- Иерархия групп и элементов: Группы содержат элементы и другие группы. Элементы не могут иметь детей. (Стандартный вариант).
- Иерархия элементов: Любой элемент может быть родителем для другого элемента (например, справочник «Подразделения», «Сотрудники» с иерархией).
Для иерархии элементов запросы выглядят идентично, но есть нюанс при использовании ИТОГИ ПО … ИЕРАРХИЯ: элемент одновременно является и строкой данных, и «группой» для своих детей. В ДеревеЗначений это приведет к тому, что элемент будет дублироваться: как узел-детей и как итоговая строка. Обычно это решается пост-обработкой дерева или использованием только плоской выборки с ручной сборкой дерева.
Производительность и лучшие практики
- Индексы: Убедитесь, что по полю Родитель построен индекс (по умолчанию платформа создает его автоматически для иерархических справочников).
- Отбор ранний: Всегда старайтесь отбирать ветку через В ИЕРАРХИИ(&Группа) в секции ГДЕ, а не фильтровать всю таблицу справочника в коде 1С.
- Избегайте «SELECT *»: Выбирайте только нужные поля (Ссылка, Наименование, Родитель, ЭтоГруппа, нужные реквизиты).
- Временные таблицы: Для сложных отчетов, где нужна иерархия в сочетании с движением регистров, используйте временные таблицы (ПОМЕСТИТЬ ВТ_Иерархия). Сначала соберите иерархию во ВТ, потом соединяйте с регистрами.
- Параметры: Всегда передавайте ссылки на группы через параметры запроса (УстановитьПараметр), а не склеивайте строки запроса. Это безопаснее и позволяет кэшировать план запроса.
Резюме: какой способ выбрать?
| Задача | Рекомендуемый метод |
|---|---|
| Получить все элементы ветки (плоский список) | ВЫБРАТЬ … ГДЕ Ссылка В ИЕРАРХИИ(&Группа) |
| Вывести дерево на форму / в Табличный документ | ИТОГИ ПО Ссылка ИЕРАРХИЯ АВТОУПОРЯДОЧИВАНИЕ + Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией) |
| Получить только корневые группы | ГДЕ Родитель = ЗНАЧЕНИЕ(Справочник.Имя.ПустаяСсылка) или В ИЕРАРХИИ(ПустаяСсылка) |
| Построить путь (Full Path) к каждому элементу | Выборка в ТаблицуЗначений + программный обход вверх по Родителю (или динамический Self-Join для фиксированной глубины) |
| Плоский отчет с колонками «Уровень 1», «Уровень 2»..; | Динамическое формирование запроса с множественными LEFT JOIN (Self-Join) после определения макс. глубины |
Язык запросов 1С предоставляет мощные декларативные средства для работы с иерархией: конструкция В ИЕРАРХИИ для отбора и ИТОГИ ПО … ИЕРАРХИЯ для упорядочивания и выгрузки в дерево. Понимание того, как платформа строит иерархию на основе поля Родитель при выгрузке с ОбходРезультатаЗапроса.ПоГруппировкамСИерархией, позволяет избежать написания рекурсивных функций на встроенном языке в 90% типовых задач. Для специфических задач (пути, плоские таблицы с неизвестной глубиной) успешно применяется комбинация запросов во временные таблицы и последующей обработки на стороне клиента/сервера 1С.