Как в запросе 1С отсортировать иерархический справочник по списку групп и их иерархии?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов и обработок в 1С часто возникает задача: выбрать элементы справочника (например, номенклатуру или статьи затрат) по условию В ИЕРАРХИИ(&СписокГрупп) так, чтобы результат был выстроен строго по порядку переданных групп, а внутри каждой группы сохранялась древовидная иерархия подчинения.

Разберем, с какими ограничениями платформы 1С приходится сталкиваться в подобных запросах, почему стандартная сортировка ведет себя иначе и как правильно решить данную задачу несколькими способами.

Ограничения языка запросов 1С при работе с иерархией

Проанализируем основные технические нюансы, которые влияют на выбор архитектуры решения:

  1. Поведение конструкции УПОРЯДОЧИТЬ ПО Ссылка ИЕРАРХИЯ. Данная конструкция упорядочивает дерево элементов исключительно по топологическому порядку самого справочника (от корня к листьям с сортировкой веток по коду или наименованию). Она не учитывает порядок следования элементов внутри переданного массива или списка значений &СписокГрупп.
  2. Ограничение соединения таблиц по иерархии. В языке запросов 1С оператор В ИЕРАРХИИ запрещено использовать в секции соединения ПО (например, конструкция ПО Таблица1.Ссылка В ИЕРАРХИИ (Таблица2.Ссылка) вызовет синтаксическую ошибку). Он допустим только в секциях ГДЕ и ИМЕЮЩИЕ.
  3. Проблемы производительности при переборе Родитель.Родитель.... Попытка определить принадлежность к группе через конструкции ВЫБОР КОГДА Номенклатура.Родитель В (...) КОГДА Номенклатура.Родитель.Родитель В (...) имеет жесткие ограничения по глубине вложенности, не масштабируется и приводит к тяжелым сканированиям таблиц на уровне СУБД.

Способ 1. Динамическое формирование текста запроса с объединением (ОБЪЕДИНИТЬ ВСЕ)

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

Посмотрим на пример формирования такого запроса в коде:


ТекстЗапроса = "";
МассивГрупп = Новый Массив;
// Заполняем массив групп в требуемом порядке
МассивГрупп.Добавить(Группа1);
МассивГрупп.Добавить(Группа2);

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

Для Индекс = 0 По МассивГрупп.Количество() - 1 Цикл
    ИмяПараметра = "Группа_" + Формат(Индекс, "ЧГ=0");
    Запрос.УстановитьПараметр(ИмяПараметра, МассивГрупп[Индекс]);
    
    Если Индекс > 0 Тогда
        ТекстЗапроса = ТекстЗапроса + "
        |ОБЪЕДИНИТЬ ВСЕ
        |";
    КонецЕсли;
    
    ТекстЗапроса = ТекстЗапроса + "
    |ВЫБРАТЬ
    |   " + Формат(Индекс, "ЧГ=0") + " КАК ПорядокГруппы,
    |   Номенклатура.Ссылка КАК Ссылка,
    |   Номенклатура.Наименование КАК Наименование
    |ИЗ
    |   Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |   Номенклатура.Ссылка В ИЕРАРХИИ(&" + ИмяПараметра + ")";
КонецЦикла;

ТекстЗапроса = ТекстЗапроса + "
|
|УПОРЯДОЧИТЬ ПО
|   ПорядокГруппы,
|   Ссылка ИЕРАРХИЯ";

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

Разберем, почему этот вариант эффективен: СУБД применяет специализированные индексы иерархии для каждой отдельной группы, а результирующая сортировка по полю ПорядокГруппы гарантирует, что данные первой группы будут выведены целиком раньше данных второй группы.

Способ 2. Выборка данных регистров с левым соединением справочника

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

Посмотрим на пример запроса:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Ссылка,
    Остатки.КоличествоОстаток КАК КоличествоОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
        ПО Остатки.Номенклатура = Номенклатура.Ссылка
ГДЕ
    Номенклатура.Ссылка В ИЕРАРХИИ(&ВыбНом)
УПОРЯДОЧИТЬ ПО
    Ссылка ИЕРАРХИЯ

Обратим внимание: в секции УПОРЯДОЧИТЬ ПО необходимо указывать именно поле Ссылка из таблицы справочника, иначе платформа не сможет построить дерево иерархии.

Способ 3. Использование итогов и древовидного обхода выборки

Рассмотрим вариант, когда результат запроса обрабатывается программно через дерево значений или многоуровневую выборку. В этом случае мы можем построить иерархию через секцию ИТОГИ:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура,
    Номенклатура.Артикул КАК Артикул
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Ссылка В ИЕРАРХИИ(&ВыбНом)
ИТОГИ ПО
    Номенклатура ИЕРАРХИЯ

При обходе результата запроса в коде используем метод Выбрать с типом обхода ОбходРезультатаЗапроса.ПоГруппировкамСИерархией либо выгружаем данные методом РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией) в объект ДеревоЗначений. Если корневые узлы дерева требуется расположить в определенном порядке, достаточно обойти верхний уровень дерева или списка групп и программно расположить строки в нужном порядке.

Способ 4. Использование Системы компоновки данных (СКД)

Если задача решается в рамках построения отчета, наиболее лаконичным решением является использование СКД:

  1. В схеме компоновки данных поле номенклатуры выводится в группировку с типом Иерархия.
  2. Порядок корневых групп настраивается на вкладке Сортировка с помощью пользовательских полей-выражений или внешнего набора данных, где каждой группе сопоставлен порядковый номер.
  3. СКД автоматически распределит подчиненные элементы внутри каждой родительской ветки с сохранением древовидной структуры.
← На главную