При формировании прайс-листов и специализированных отчетов часто возникает задача пользовательской сортировки номенклатуры. Пользователю предоставляется возможность задавать порядковый номер для отдельных групп или элементов. При этом подчиненные позиции должны автоматически наследовать порядок родительской группы, если для них не указан собственный номер.
Рассмотрим, с какими ограничениями платформы 1С приходится сталкиваться при решении этой задачи и какие архитектурные подходы позволяют получить корректный результат без потери производительности системы.
Разберем, почему выполнить такую операцию простым запросом по произвольной иерархии бывает затруднительно:
WITH RECURSIVE в SQL) для прохода по неизвестному количеству уровней иерархии вверх.ИТОГИ ПО ... ИЕРАРХИЯ строит дерево, но оператор ПОМЕСТИТЬ не работает совместно с секцией ИТОГИ. Вы не сможете сохранить результат с иерархическими итогами во временную таблицу для последующего соединения.В ИЕРАРХИИ: Эту конструкцию разрешено использовать только в секции ГДЕ или в операторе ВЫБОР КОГДА, но ее нельзя применить непосредственно в условии соединения таблиц ПО.Проанализировав производительность, простейшим и наиболее быстрыми решением является отказ от динамического расчета наследования в момент формирования отчета. Динамические вычисления на тысячах позиций приводят к существенным задержкам.
Для реализации этого подхода выполним следующие шаги:
ПорядокНоменклатуры) с измерениями Номенклатура и ресурсом Порядок.ПриЗаписи для справочника Номенклатура. При изменении родителя или создании элемента система будет определять итоговый порядок с учетом наследования от верхнего уровня и записывать его в регистр.Посмотрим на пример алгоритма обхода справочника при первоначальном заполнении регистра:
Процедура ЗаполнитьПорядокИерархически(Знач ТекущийРодитель, Знач ПорядокРодителя)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| ЕСТЬНУЛЛ(ПорядокРС.Порядок, 0) КАК СвойПорядок
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПорядокНоменклатуры КАК ПорядокРС
| ПО Номенклатура.Ссылка = ПорядокРС.Номенклатура
|ГДЕ
| Номенклатура.Родитель = &Родитель";
Запрос.УстановитьПараметр("Родитель", ТекущийРодитель);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Если у элемента задан свой порядок, используем его, иначе наследуем от родителя
ИтоговыйПорядок = ?(Выборка.СвойПорядок > 0, Выборка.СвойПорядок, ПорядокРодителя);
Если ИтоговыйПорядок = 0 Тогда
ИтоговыйПорядок = 999; // Значение по умолчанию для позиций без порядка
КонецЕсли;
ЗаписатьПорядокВРегистр(Выборка.Ссылка, ИтоговыйПорядок);
// Рекурсивно обрабатываем подчиненные элементы
ЗаполнитьПорядокИерархически(Выборка.Ссылка, ИтоговыйПорядок);
КонецЦикла;
КонецПроцедуры
Если структура справочника имеет жесткое ограничение по уровню вложенности (например, не более 3–4 уровней), наследование порядка можно вычислить непосредственно в запросе с помощью последовательного соединения родительских узлов.
Рассмотрим пример запроса для справочника с уровнем вложенности до 3 уровней:
ВЫБРАТЬ
Ном.Ссылка КАК Номенклатура,
ЕСТЬНУЛЛ(ПорядокСобственный.Порядок,
ЕСТЬНУЛЛ(ПорядокРодителя1.Порядок,
ЕСТЬНУЛЛ(ПорядокРодителя2.Порядок, 999))) КАК ИтоговыйПорядок
ИЗ
Справочник.Номенклатура КАК Ном
// Порядок самого элемента
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПорядокНоменклатуры КАК ПорядокСобственный
ПО Ном.Ссылка = ПорядокСобственный.Номенклатура
// Порядок родителя 1-го уровня
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПорядокНоменклатуры КАК ПорядокРодителя1
ПО Ном.Родитель = ПорядокРодителя1.Номенклатура
// Порядок родителя 2-го уровня
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПорядокНоменклатуры КАК ПорядокРодителя2
ПО Ном.Родитель.Родитель = ПорядокРодителя2.Номенклатура
В данном запросе функция ЕСТЬНУЛЛ проверяет наличие заданного порядка сначала у самого элемента. Если порядок не задан, вычисляется значение ближайшего родителя, а при его отсутствии присваивается значение по умолчанию 999.
Если конечной целью является вывод отчета, а не поместить данные во временную таблицу для последующего соединения, с задачей отлично справляется Система компоновки данных (СКД).
Выясним, как настроить связывание наборов данных:
Ссылка, в качестве приемника — Родитель.Для задач построения прайс-листов и высоконагруженных отчетов наиболее оптимальным является подход с хранением подготовленного порядка в базе данных (Решение 1). Это избавляет систему от вычисления иерархических связей при каждом формировании документа или запроса, обеспечивая максимальное быстродействие.