При разработке часто возникает задача: выбрать определенный узел иерархического справочника (группу или элемент) и получить все подчиненные ему данные на любую глубину вложенности с наложением дополнительных условий. Начинающие разработчики нередко пытаются реализовать для этого рекурсивный обход дерева через объектную модель. Однако в платформе 1С:Предприятие для таких задач предусмотрен штатный и гораздо более быстрый механизм в языке запросов.
Разберем по шагам, как правильно строить запросы для выборки подчиненных элементов, какие нюансы работы с иерархией важно учитывать и как избежать типичных ошибок при фильтрации данных.
Проанализируем ситуацию, описанную в теме: у нас есть иерархический справочник, и требуется получить все подчиненные элементы для выбранной ветки (например, найти конкретные свойства или элементы, расположенные ниже определенного родительского узла). Для этого не нужно писать собственные алгоритмы рекурсии. В языке запросов 1С существует специальный оператор сравнения В ИЕРАРХИИ.
Посмотрим на пример базового запроса:
ВЫБРАТЬ
Игрушки.Ссылка КАК Ссылка,
Игрушки.Наименование КАК Наименование
ИЗ
Справочник.Игрушки КАК Игрушки
ГДЕ
Игрушки.Ссылка В ИЕРАРХИИ(&РодительскийУзел)
В параметр &РодительскийУзел передается ссылка на элемент или группу, начиная с которой требуется спуститься вниз по дереву. Оператор В ИЕРАРХИИ автоматически обойдет всю ветку на любую глубину и вернет все дочерние записи.
Рассмотрим важную особенность: конструкция В ИЕРАРХИИ по умолчанию включает в результат не только всех потомков, но и сам исходный узел, переданный в качестве параметра. Если задача требует получить строго подчиненные элементы, исходный родитель окажется лишним.
Выясним причину и устраним ее с помощью простого дополнительного условия в секции ГДЕ:
ВЫБРАТЬ
Игрушки.Ссылка КАК Ссылка,
Игрушки.Наименование КАК Наименование
ИЗ
Справочник.Игрушки КАК Игрушки
ГДЕ
Игрушки.Ссылка В ИЕРАРХИИ(&РодительскийУзел)
И Игрушки.Ссылка <> &РодительскийУзел
Если в качестве параметра &СписокРодителей передается массив или список значений, исключение исходных узлов выполняется через отрицание списка: И НЕ Игрушки.Ссылка В (&СписокРодителей).
При работе со справочниками в 1С критически важно понимать, какой тип иерархии настроен в метаданных конфигурации:
В ИЕРАРХИИ платформа вернет и вложенные группы, и конечные элементы. Если нам требуются только конкретные конечные элементы (без промежуточных папок), необходимо явно добавить проверку стандартного реквизита: И НЕ Игрушки.ЭтоГруппа (или И Игрушки.ЭтоГруппа = ЛОЖЬ).ЭтоГруппа отсутствует, а проверка не требуется.Посмотрим на пример комплексного запроса, где мы выбираем только конечные элементы ветки с наложением дополнительного фильтра (например, по наименованию или реквизиту):
ВЫБРАТЬ
Игрушки.Ссылка КАК Ссылка,
Игрушки.Наименование КАК Наименование
ИЗ
Справочник.Игрушки КАК Игрушки
ГДЕ
Игрушки.Ссылка В ИЕРАРХИИ(&РодительскийУзел)
И Игрушки.Ссылка <> &РодительскийУзел
И НЕ Игрушки.ЭтоГруппа
И Игрушки.Наименование ПОДОБНО &МаскаПоиска
Рассмотрим подробнее сценарий, когда не требуется спускаться на всю глубину дерева, а нужны только прямые потомки первого уровня. Использовать конструкцию В ИЕРАРХИИ в таком случае избыточно и неэффективно с точки зрения производительности.
Для выборки только прямых детей достаточно проверить стандартный реквизит Родитель через прямое равенство:
ВЫБРАТЬ
Игрушки.Ссылка КАК Ссылка,
Игрушки.Наименование КАК Наименование
ИЗ
Справочник.Игрушки КАК Игрушки
ГДЕ
Игрушки.Родитель = &РодительскийУзел
И НЕ Игрушки.ЭтоГруппа
Проанализируем работу запроса на уровне базы данных, чтобы избежать проблем с быстродействием на крупных справочниках:
В ИЕРАРХИИ транслируется платформой в рекурсивные табличные выражения или сложные соединения на стороне СУБД. На справочниках с сотнями тысяч элементов и большой глубиной вложенности такие запросы могут выполняться относительно тяжело.Ссылка В ИЕРАРХИИ (ВЫБРАТЬ Ссылка ИЗ ВТ_Родители).ГДЕ вместе с иерархией, должны иметь свойство «Индексировать» в метаданных, чтобы оптимизатор СУБД мог эффективно сузить выборку.Справочники.Игрушки.Выбрать(Родитель) выбирает подчиненные элементы только одного уровня. Попытка организовать рекурсивный обход дерева программным кодом приводит к сотням отдельных запросов к СУБД (проблема N+1), поэтому для любых массовых выборок следует использовать исключительно единый запрос с оператором В ИЕРАРХИИ.