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