При разработке отчетов и обработок в 1С часто возникает задача: выбрать элементы справочника (например, номенклатуру или статьи затрат) по условию В ИЕРАРХИИ(&СписокГрупп) так, чтобы результат был выстроен строго по порядку переданных групп, а внутри каждой группы сохранялась древовидная иерархия подчинения.
Разберем, с какими ограничениями платформы 1С приходится сталкиваться в подобных запросах, почему стандартная сортировка ведет себя иначе и как правильно решить данную задачу несколькими способами.
Проанализируем основные технические нюансы, которые влияют на выбор архитектуры решения:
УПОРЯДОЧИТЬ ПО Ссылка ИЕРАРХИЯ. Данная конструкция упорядочивает дерево элементов исключительно по топологическому порядку самого справочника (от корня к листьям с сортировкой веток по коду или наименованию). Она не учитывает порядок следования элементов внутри переданного массива или списка значений &СписокГрупп.В ИЕРАРХИИ запрещено использовать в секции соединения ПО (например, конструкция ПО Таблица1.Ссылка В ИЕРАРХИИ (Таблица2.Ссылка) вызовет синтаксическую ошибку). Он допустим только в секциях ГДЕ и ИМЕЮЩИЕ.Родитель.Родитель.... Попытка определить принадлежность к группе через конструкции ВЫБОР КОГДА Номенклатура.Родитель В (...) КОГДА Номенклатура.Родитель.Родитель В (...) имеет жесткие ограничения по глубине вложенности, не масштабируется и приводит к тяжелым сканированиям таблиц на уровне СУБД.Рассмотрим наиболее надежный и производительный способ в случаях, когда список групп формируется программно во встроенном языке. Мы можем динамически сформировать текст запроса, где для каждой группы из списка создается своя ветка выборки с явным указанием весового коэффициента (индекса порядка).
Посмотрим на пример формирования такого запроса в коде:
ТекстЗапроса = "";
МассивГрупп = Новый Массив;
// Заполняем массив групп в требуемом порядке
МассивГрупп.Добавить(Группа1);
МассивГрупп.Добавить(Группа2);
Запрос = Новый Запрос;
Для Индекс = 0 По МассивГрупп.Количество() - 1 Цикл
ИмяПараметра = "Группа_" + Формат(Индекс, "ЧГ=0");
Запрос.УстановитьПараметр(ИмяПараметра, МассивГрупп[Индекс]);
Если Индекс > 0 Тогда
ТекстЗапроса = ТекстЗапроса + "
|ОБЪЕДИНИТЬ ВСЕ
|";
КонецЕсли;
ТекстЗапроса = ТекстЗапроса + "
|ВЫБРАТЬ
| " + Формат(Индекс, "ЧГ=0") + " КАК ПорядокГруппы,
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В ИЕРАРХИИ(&" + ИмяПараметра + ")";
КонецЦикла;
ТекстЗапроса = ТекстЗапроса + "
|
|УПОРЯДОЧИТЬ ПО
| ПорядокГруппы,
| Ссылка ИЕРАРХИЯ";
Запрос.Текст = ТекстЗапроса;
РезультатЗапроса = Запрос.Выполнить();
Разберем, почему этот вариант эффективен: СУБД применяет специализированные индексы иерархии для каждой отдельной группы, а результирующая сортировка по полю ПорядокГруппы гарантирует, что данные первой группы будут выведены целиком раньше данных второй группы.
Если данные выбираются не из самого справочника, а из виртуальной или реальной таблицы регистра (например, регистра накопления), для работы иерархической сортировки обязательно необходимо присоединять сам справочник.
Посмотрим на пример запроса:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка,
Остатки.КоличествоОстаток КАК КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
ПО Остатки.Номенклатура = Номенклатура.Ссылка
ГДЕ
Номенклатура.Ссылка В ИЕРАРХИИ(&ВыбНом)
УПОРЯДОЧИТЬ ПО
Ссылка ИЕРАРХИЯ
Обратим внимание: в секции УПОРЯДОЧИТЬ ПО необходимо указывать именно поле Ссылка из таблицы справочника, иначе платформа не сможет построить дерево иерархии.
Рассмотрим вариант, когда результат запроса обрабатывается программно через дерево значений или многоуровневую выборку. В этом случае мы можем построить иерархию через секцию ИТОГИ:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура,
Номенклатура.Артикул КАК Артикул
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Ссылка В ИЕРАРХИИ(&ВыбНом)
ИТОГИ ПО
Номенклатура ИЕРАРХИЯ
При обходе результата запроса в коде используем метод Выбрать с типом обхода ОбходРезультатаЗапроса.ПоГруппировкамСИерархией либо выгружаем данные методом РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией) в объект ДеревоЗначений. Если корневые узлы дерева требуется расположить в определенном порядке, достаточно обойти верхний уровень дерева или списка групп и программно расположить строки в нужном порядке.
Если задача решается в рамках построения отчета, наиболее лаконичным решением является использование СКД: