Почему слетает упорядочивание по иерархии в запросе при отборе элементов и как это исправить?

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

При разработке отчетов и обработок в 1С часто возникает задача: вывести список элементов справочника (например, номенклатуры) так, чтобы они следовали строго в порядке их реального нахождения в группах каталога, но при этом сами строки-группы (папки) в итоговом списке показывать не нужно. Разработчики нередко пытаются решить эту задачу «в лоб» — добавляют в запрос конструкцию УПОРЯДОЧИТЬ ПО ... ИЕРАРХИЯ и одновременно отсекают папки условием ГДЕ ЭтоГруппа = ЛОЖЬ. Однако в результате сортировка полностью нарушается. Разберем механику этой проблемы и рассмотрим несколько корректных способов её решения.

Проанализируем причину: как работает сортировка по иерархии

Выясним причину некорректного поведения. Конструкция запроса УПОРЯДОЧИТЬ ПО Поле ИЕРАРХИЯ предназначена для выстраивания записей в порядке классического обхода дерева: сначала выводится родительский узел, затем все подчиненные ему дочерние узлы и элементы. Для корректного построения такой последовательности платформе 1С требуются все звенья цепочки подчинения.

Секция ГДЕ выполняется до того, как результат упорядочивается. Когда мы пишем условие Номенклатура.ЭтоГруппа = ЛОЖЬ, платформа физически исключает все папки из выборки. Дерево связей оказывается разрушенным: у дочерних элементов больше нет родительских записей в результирующем наборе данных. Механизм иерархического упорядочивания теряет точки опоры, и результирующий порядок строк становится хаотичным либо сводится к обычной сортировке по наименованию без учета веток.

Кроме того, обратим внимание на типичную ошибку производительности при получении полного пути к элементу. Если в цикле обхода выборки вызывать метод Ссылка.ПолноеНаименование(), платформа будет на каждой итерации обращаться к базе данных и считывать весь объект целиком. При большом объеме справочника это вызовет проблему N+1 запросов и приведет к критическому падению скорости работы алгоритма. Посмотрим, как решить задачу правильно.

Способ 1. Фильтрация папок на этапе обхода результата запроса

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

Разберем реализацию по шагам:

  1. Убираем из секции ГДЕ условие Номенклатура.ЭтоГруппа = ЛОЖЬ.
  2. Оставляем конструкцию УПОРЯДОЧИТЬ ПО Номенклатура.Наименование ИЕРАРХИЯ.
  3. При обходе выборки проверяем признак ЭтоГруппа и переходим к следующей записи.

Посмотрим на пример текста запроса и последующего обхода:


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

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
    Если Выборка.ЭтоГруппа Тогда
        Продолжить;
    КонецЕсли;
    
    // Обработка только конечных элементов в порядке иерархии
    Сообщить(Выборка.Наименование);
КонецЦикла;

В этом случае платформа корректно выстроит последовательность по иерархическому дереву, а пользователь получит только целевые элементы без «голых» папок.

Способ 2. Использование секции ИТОГИ ПО ... ТОЛЬКО ИЕРАРХИЯ

Если мы не хотим получать папки в качестве обычных записей детального уровня или нам требуется древовидный обход, воспользуемся иерархическими итогами. Конструкция ИТОГИ ПО ... ТОЛЬКО ИЕРАРХИЯ позволяет оставить фильтрацию детальных записей в секции ГДЕ — платформа сама построит недостающие родительские уровни в виде итоговых строк.

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Ссылка,
    |	Номенклатура.Наименование КАК Наименование
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |	Номенклатура.ЭтоГруппа = ЛОЖЬ
    |	И Номенклатура.ПометкаУдаления = ЛОЖЬ
    |ИТОГИ ПО
    |	Ссылка ТОЛЬКО ИЕРАРХИЯ";

Результат = Запрос.Выполнить();
ВыборкаИерархия = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);

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

Способ 3. Сортировка по цепочке родителей для плоского списка

Если структура справочника имеет фиксированную и небольшую вложенность (например, до двух-трех уровней), мы можем полностью отказаться от иерархического упорядочивания и отсортировать только элементы. Для этого явно перечислим родителей в секции УПОРЯДОЧИТЬ ПО через точку.

Рассмотрим структуру такого запроса:


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

УПОРЯДОЧИТЬ ПО
	Номенклатура.Родитель.Родитель.Наименование,
	Номенклатура.Родитель.Наименование,
	Номенклатура.Наименование

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

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

Если конечная цель разработки — отчет для пользователя, наиболее производительным и грамотным решением будет использование Системы компоновки данных (СКД). Механизм компоновки спроектирован специально для решения подобных задач и избавляет от необходимости вручную формировать строковые пути видов «Папка/Подпапка/Товар».

Для корректного вывода достаточно:

  1. Передать в набор данных запроса поле Ссылка без жестких условий сортировки.
  2. В настройках структуры отчета добавить группировку по полю Ссылка.
  3. В свойствах группировки переключить параметр Тип группировки со значения Элементы на Иерархия или Только иерархия в зависимости от требуемого макета.

Платформа возьмет на себя оптимальное упорядочивание, построение визуального дерева и расчет итогов без падения производительности.

← На главную