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