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

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

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

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

Причины низкой производительности при соединении по родителям

Разберем типичную ошибку при формировании запроса, когда связывание элементов справочника с родителями выполняется через оператор ИЛИ по всем возможным уровням вложенности:


ВЫБРАТЬ РАЗЛИЧНЫЕ
	ГруппыНоменклатуры.ГруппаНоменклатуры
ПОМЕСТИТЬ Родители
ИЗ
	&ГруппыНоменклатуры КАК ГруппыНоменклатуры;

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

Проанализируем, почему такой код работает крайне медленно:

  1. Использование оператора ИЛИ в секции соединения ПО: СУБД не может задействовать индексы по полю Родитель, что заставляет сервер выполнять сканирование всей таблицы (Table Scan).
  2. Ограничение оператора В ИЕРАРХИИ: Платформа 1С не позволяет применять конструкцию В ИЕРАРХИИ непосредственно внутри условий соединения ПО, а только в секции ГДЕ или ИМЕЮЩИЕ.

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

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

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

  1. Создаем Набор данных 1 (Планы): Выбираем плановые показатели и список родительских групп из табличной части документа или источника.
    
    ВЫБРАТЬ РАЗЛИЧНЫЕ
    	СТ_ПланыПродаж.План КАК План,
    	СТ_ПланыПродаж.ГруппаНоменклатуры КАК ГруппаНоменклатуры
    ИЗ
    	Документ.СТ_ПланыПродаж.Планы КАК СТ_ПланыПродаж
    ГДЕ
    	СТ_ПланыПродаж.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода;
    
  2. Создаем Набор данных 2 (Продажи): Выбираем фактические продажи в разрезе конечных элементов номенклатуры с использованием параметра для фильтрации по иерархии.
    
    ВЫБРАТЬ
    	ПродажиОбороты.Номенклатура КАК Номенклатура,
    	ПродажиОбороты.СуммаОборот КАК СуммаFact
    ИЗ
    	РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , Номенклатура В ИЕРАРХИИ (&ГруппаНоменклатуры)) КАК ПродажиОбороты;
    
  3. Настраиваем связи наборов данных: Переходим на вкладку Связи наборов данных и добавляем связь:
    • Источник связи: Набор данных 1
    • Приемник связи: Набор данных 2
    • Выражение источник: ГруппаНоменклатуры
    • Параметр: ГруппаНоменклатуры

В этом случае СКД автоматически выполнит запрос Набора 2 для каждой группы из Набора 1, подставляя значение группы в параметр иерархии, что гарантирует высокую скорость работы и корректный выбор всех подчиненных элементов.

Способ 2. Транзитивное замыкание иерархии в запросе

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

Рассмотрим пример каскадного получения всех вышестоящих родителей для каждого элемента справочника Справочник.Номенклатура:


ВЫБРАТЬ
	Номенклатура.Ссылка КАК Элемент,
	ЕСТЬNULL(Уровень1.Родитель, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК Родитель1,
	ЕСТЬNULL(Уровень2.Родитель, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК Родитель2,
	ЕСТЬNULL(Уровень3.Родитель, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК Родитель3
ПОМЕСТИТЬ ТЗ_Иерархия
ИЗ
	Справочник.Номенклатура КАК Номенклатура
	ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Уровень1
		ПО Номенклатура.Родитель = Уровень1.Ссылка
	ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Уровень2
		ПО Уровень1.Родитель = Уровень2.Ссылка
	ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Уровень3
		ПО Уровень2.Родитель = Уровень3.Ссылка
ГДЕ
	НЕ Номенклатура.ЭтоГруппа;

Каждый следующий каскад удваивает разворачиваемое количество уровней вложенности. Развернув иерархию во временную таблицу, мы можем легко выполнить точное соединение по полю равенства (без операторов ИЛИ), получив высокую скорость обработки даже на больших объемах данных.

Способ 3. Расчет итогов с помощью конструктора иерархии

Если задача заключается в получении суммовых показателей по иерархии без жесткой привязки к документу плана, достаточно сформировать стандартный запрос по элементам и применить секцию ИТОГИ:


ВЫБРАТЬ
	ПродажиОбороты.Номенклатура КАК Номенклатура,
	ПродажиОбороты.СуммаОборот КАК Сумма
ИЗ
	РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ПродажиОбороты
ИТОГИ
	СУММА(Сумма)
ПО
	Номенклатура ИЕРАРХИЯ;

Платформа 1С на уровне СУБД самостоятельно рассчитает агрегированные данные для всех родительских групп, избавив от необходимости ручного связывания уровней.

← На главную