Как выгрузить иерархический справочник с наследованием порядка сортировки в 1С

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

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

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

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

Разберем, почему выполнить такую операцию простым запросом по произвольной иерархии бывает затруднительно:

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

Решение 1. Поддержание порядка в базе данных (Оптимальный подход)

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

Для реализации этого подхода выполним следующие шаги:

  1. Создадим регистр сведений (например, ПорядокНоменклатуры) с измерениями Номенклатура и ресурсом Порядок.
  2. Создадим подписку на событие ПриЗаписи для справочника Номенклатура. При изменении родителя или создании элемента система будет определять итоговый порядок с учетом наследования от верхнего уровня и записывать его в регистр.
  3. Первоначальное заполнение регистра выполним специальной обработкой, которая обойдет дерево справочника сверху вниз.

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


Процедура ЗаполнитьПорядокИерархически(Знач ТекущийРодитель, Знач ПорядокРодителя)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	Номенклатура.Ссылка КАК Ссылка,
        |	ЕСТЬНУЛЛ(ПорядокРС.Порядок, 0) КАК СвойПорядок
        |ИЗ
        |	Справочник.Номенклатура КАК Номенклатура
        |	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПорядокНоменклатуры КАК ПорядокРС
        |		ПО Номенклатура.Ссылка = ПорядокРС.Номенклатура
        |ГДЕ
        |	Номенклатура.Родитель = &Родитель";
        
    Запрос.УстановитьПараметр("Родитель", ТекущийРодитель);
    Выборка = Запрос.Выполнить().Выбрать();
    
    Пока Выборка.Следующий() Цикл
        // Если у элемента задан свой порядок, используем его, иначе наследуем от родителя
        ИтоговыйПорядок = ?(Выборка.СвойПорядок > 0, Выборка.СвойПорядок, ПорядокРодителя);
        
        Если ИтоговыйПорядок = 0 Тогда
            ИтоговыйПорядок = 999; // Значение по умолчанию для позиций без порядка
        КонецЕсли;
        
        ЗаписатьПорядокВРегистр(Выборка.Ссылка, ИтоговыйПорядок);
        
        // Рекурсивно обрабатываем подчиненные элементы
        ЗаполнитьПорядокИерархически(Выборка.Ссылка, ИтоговыйПорядок);
    КонецЦикла;
    
КонецПроцедуры

Решение 2. Каскадные левые соединения в запросе

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

Рассмотрим пример запроса для справочника с уровнем вложенности до 3 уровней:


ВЫБРАТЬ
	Ном.Ссылка КАК Номенклатура,
	ЕСТЬНУЛЛ(ПорядокСобственный.Порядок,
		ЕСТЬНУЛЛ(ПорядокРодителя1.Порядок,
			ЕСТЬНУЛЛ(ПорядокРодителя2.Порядок, 999))) КАК ИтоговыйПорядок
ИЗ
	Справочник.Номенклатура КАК Ном
	
	// Порядок самого элемента
	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПорядокНоменклатуры КАК ПорядокСобственный
		ПО Ном.Ссылка = ПорядокСобственный.Номенклатура
		
	// Порядок родителя 1-го уровня
	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПорядокНоменклатуры КАК ПорядокРодителя1
		ПО Ном.Родитель = ПорядокРодителя1.Номенклатура
		
	// Порядок родителя 2-го уровня
	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПорядокНоменклатуры КАК ПорядокРодителя2
		ПО Ном.Родитель.Родитель = ПорядокРодителя2.Номенклатура

В данном запросе функция ЕСТЬНУЛЛ проверяет наличие заданного порядка сначала у самого элемента. Если порядок не задан, вычисляется значение ближайшего родителя, а при его отсутствии присваивается значение по умолчанию 999.

Решение 3. Использование связей наборов данных в СКД

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

Выясним, как настроить связывание наборов данных:

  1. В схеме компоновки данных создадим один набор данных - запрос, выбирающий номенклатуру и заданный порядок из регистра.
  2. Перейдем на вкладку Связи наборов данных.
  3. Добавим связь набора данных к самому себе: в качестве источника укажем Ссылка, в качестве приемника — Родитель.
  4. В поле выражений укажем пересчет значения порядка по ветке иерархии. СКД автоматически выполнит рекурсивный подъем по дереву и подставит унаследованные значения.

Итоги

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

← На главную