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