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

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

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

Причины сложности выборки иерархических данных в запросе

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

Обычное левое соединение справочника с регистром по условию совпадения ссылки вернет значение только для тех групп, которые явно внесены в регистр. Попытка соединить таблицы по условию ИЛИ (например, Ссылка = Наценка.Номенклатура ИЛИ Ссылка.Родитель = Наценка.Номенклатура) приводит к падению производительности из-за неэффективного использования индексов СУБД и некорректному дублированию строк.

Решение 1. Каскадное левое соединение таблиц с использованием функции ЕСТЬNULL

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

Разберем пример построения такого запроса:


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

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

  1. Проверка уровней по порядку: Запрос проверяет наличие явно установленной наценки сначала у самого элемента (РС_Уровень0), затем у его непосредственного родителя (РС_Уровень1), затем у родителя родителя и так далее.
  2. Соблюдение приоритета: Вложенная функция ЕСТЬNULL выбирает первое непустое значение слева направо. Таким образом, если наценка задана и для верхней родительской группы, и для вложенной подгруппы, запрос вернет значение вложенной подгруппы.
  3. Безопасная обработка верхушки иерархии: Если у элемента меньше уровней иерархии, чем указано в запросе, обращение через точку к несуществующим родителям (например, Родитель.Родитель) возвращает значение NULL, не вызывая ошибку платформы.

Решение 2. Архитектура типовых решений на примере подсистемы счетов учета номенклатуры

Посмотрим, как аналогичные задачи решаются в типовых конфигурациях (например, в «1С:Бухгалтерии предприятия 8»).

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

Логика работы стандартного алгоритма включает следующие шаги:

  1. Формируется массив ссылок, включающий сам элемент и все его родительские группы снизу вверх по иерархии.
  2. Выполняется пакетный запрос к регистру сведений с отбором по сформированному массиву ссылок.
  3. В программном коде BSL выбирается запись, находящаяся ближе всего к целевому элементу (с наивысшим приоритетом вложенности).

Решение 3. Оптимизированный программный обход в BSL с использованием объекта Соответствие

Если справочник имеет большую вложенность или содержит десятки тысяч элементов (например, более 30 000 позиций), целесообразно использовать выгрузку данных регистра в хеш-таблицу на сервере.

Рассмотрим пример программной обработки выборки:


// Заполняем соответствие установленных наценок
КартаНаценок = Новый Соответствие;
ВыборкаРС = РегистрыСведений.НаценкиНоменклатуры.Выбрать();
Пока ВыборкаРС.Следующий() Цикл
	КартаНаценок.Вставить(ВыборкаРС.Номенклатура, ВыборкаРС.Наценка);
КонецЦикла;

// Получаем элементы справочника
Запрос = Новый Запрос;
Запрос.Текст = 
	"ВЫБРАТЬ
	|	Номенклатура.Ссылка КАК Ссылка,
	|	Номенклатура.Родитель КАК Родитель
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура
	|ГДЕ
	|	НЕ Номенклатура.ЭтоГруппа";

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
	
	НаценкаЭлемента = 0;
	ТекущаяСсылка = Выборка.Ссылка;
	
	// Поднимаемся вверх по иерархии до первого совпадения
	Пока ЗначениеЗаполнено(ТекущаяСсылка) Цикл
		НаценкаЗначение = КартаНаценок.Получить(ТекущаяСсылка);
		Если НаценкаЗначение <> Неопределено Тогда
			НаценкаЭлемента = НаценкаЗначение;
			Прервать; // Найдено значение с наивысшим приоритетом
		КонецЕсли;
		ТекущаяСсылка = ТекущаяСсылка.Родитель;
	КонецЦикла;
	
	// Значение НаценкаЭлемента получено и готово к использованию
	
КонецЦикла;

Благодаря тому, что поиск по ключу в объекте Соответствие выполняется за константное время O(1), обход десятков тысяч элементов занимает доли секунды и не создает избыточной нагрузки на сервер баз данных.

Важные нюансы и рекомендации

Проанализируем дополнительные факторы, которые необходимо учитывать при проектировании решений:

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