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

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

При разработке механизмов выгрузки данных на сайт или формировании сложных прайс-листов программисты часто сталкиваются с задачей, когда настройки (тип цен, склад, подразделение) задаются не для каждого товара отдельно, а для целых товарных групп. Ситуация осложняется, когда нам нужно получить срез последних цен из регистра, используя эти настройки как фильтры. Основная трудность заключается в том, что стандартный оператор В ИЕРАРХИИ в языке запросов 1С не всегда позволяет элегантно связать конкретную группу из таблицы настроек с её «детьми» при одновременной проверке других измерений (например, типа цены).

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

В чем заключается основная проблема?

Представим ситуацию: у нас есть таблица настроек, где для группы «Масла» установлен «Тип цен 1», а для группы «Аккумуляторы» — «Тип цен 2». Нам нужно получить актуальные цены только для элементов, входящих в эти группы, соблюдая соответствие типов цен. Если мы попытаемся использовать конструкцию ГДЕ Номенклатура В ИЕРАРХИИ (&МассивГрупп) И ТипЦен В (&МассивТипов), то получим «перекрестное» зацикливание: система может вернуть цену по «Типу цен 2» для товара из группы «Масла», если такая запись существует в регистре. Нам же нужно жесткое соответствие «Группа -> Элементы -> Тип цен».

Проанализируем ситуацию: оператор В ИЕРАРХИИ отлично работает, когда мы проверяем принадлежность одного объекта к списку родителей. Однако он не позволяет в рамках одного запроса выполнить соединение таблиц «по иерархии» так же легко, как мы делаем соединение «по ссылке».

Решение №1: Динамическое формирование текста запроса

Это наиболее прямолинейный и рабочий способ, предложенный в обсуждении. Если количество строк в таблице настроек не исчисляется тысячами, мы можем собрать текст запроса в цикле, объединяя результаты для каждой группы через ОБЪЕДИНИТЬ ВСЕ.

Рассмотрим пример кода, реализующего этот подход:


ТекстЗапроса = "";
Запрос = Новый Запрос;

Для Каждого Стр Из ТаблицаНастроек Цикл
    Номер = Стр.НомерСтроки;
    
    // Формируем фрагмент запроса для каждой строки настроек
    ТекстЗапроса = ТекстЗапроса + 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Номенклатура,
    |	&ТипЦен" + Номер + " КАК ТипЦен,
    |	&Подразделение" + Номер + " КАК Подразделение
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |	Номенклатура.Ссылка В ИЕРАРХИИ(&Группа" + Номер + ")
    |	И НЕ Номенклатура.ЭтоГруппа";
    
    // Устанавливаем параметры для каждой итерации
    Запрос.УстановитьПараметр("Группа" + Номер, Стр.НоменклатураГруппа);
    Запрос.УстановитьПараметр("ТипЦен" + Номер, Стр.ТипЦены);
    Запрос.УстановитьПараметр("Подразделение" + Номер, Стр.Подразделение);
    
    Если Номер < ТаблицаНастроек.Количество() Тогда
        ТекстЗапроса = ТекстЗапроса + "
        |ОБЪЕДИНИТЬ ВСЕ
        |";
    КонецЕсли;
КонецЦикла;

// Теперь полученную таблицу можно поместить во временную таблицу и соединить с регистром цен
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_РазвернутыеНастройки ИЗ (" + ТекстЗапроса + ") КАК Т;
| ВЫБРАТЬ
|	Цены.Номенклатура,
|	Цены.Цена
| ИЗ
|	РегистрСведений.ЦеныНоменклатуры.СрезПоследних(, (Номенклатура, ТипЦен, Подразделение) В 
|		(ВЫБРАТЬ Номенклатура, ТипЦен, Подразделение ИЗ ВТ_РазвернутыеНастройки)) КАК Цены";

Этот метод гарантирует, что для каждого товара будут подобраны именно те настройки, которые соответствуют его группе-родителю.

Решение №2: Использование временной таблицы со всеми связями

Если динамическое построение запроса кажется вам «неэлегантным», можно воспользоваться методом «развертывания» иерархии через временные таблицы. Выясним причину, по которой это может быть эффективнее: SQL-сервер лучше оптимизирует запросы с постоянным текстом.

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

  1. Поместим все уникальные группы из настроек во временную таблицу ВТ_ГруппыНастроек.
  2. Выберем все элементы справочника Номенклатура, которые входят в иерархию этих групп. При этом используем левое соединение с самой номенклатурой по условию Элемент В ИЕРАРХИИ Группа.
  3. Соединим полученную таблицу с исходными настройками по полю Группа.

Посмотрим на пример реализации такого запроса:


// 1. Подготавливаем временную таблицу с группами
"ВЫБРАТЬ Ссылка КАК Группа, ТипЦен, Подразделение ПОМЕСТИТЬ ВТ_ИсходныеНастройки ИЗ &ТЗ_Настроек КАК Т;
|
|// 2. Разворачиваем иерархию
|ВЫБРАТЬ
|	СпрНоменклатура.Ссылка КАК Номенклатура,
|	ВТ_Настройки.ТипЦен,
|	ВТ_Настройки.Подразделение
|ПОМЕСТИТЬ ВТ_ПлоскиеНастройки
|ИЗ
|	Справочник.Номенклатура КАК СпрНоменклатура
|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ИсходныеНастройки КАК ВТ_Настройки
|		ПО (СпрНоменклатура.Ссылка В ИЕРАРХИИ (ВТ_Настройки.Группа))
|ГДЕ
|	НЕ СпрНоменклатура.ЭтоГруппа;"

Важное замечание: Использование оператора В ИЕРАРХИИ в условии соединения (ПО) поддерживается платформой 1С, но может работать медленно на очень больших справочниках. В этом случае лучше сначала отобрать все подходящие товары в список, а затем фильтровать их.

Проблема множественной иерархии и приоритетов

Проанализируем ситуацию, упомянутую в сообщениях форума: что если товар входит в «Группу 2», которая, в свою очередь, входит в «Группу 1», и для обеих групп в таблице настроек заданы разные параметры? В таком случае запрос вернет две строки для одного товара.

Чтобы решить эту проблему «по-феншую», необходимо учитывать уровень вложенности. Рассмотрим подробнее: мы можем добавить в запрос поле СпрНоменклатура.Уровень() (для обычных форм) или рассчитывать глубину иерархии иными способами. Обычно бизнес-логика подразумевает, что настройка, заданная для более «глубокой» (ближней к товару) группы, имеет приоритет.

В финальной выборке мы можем использовать конструкцию МАКСИМУМ(Уровень) и группировку по номенклатуре, чтобы оставить только одну — самую актуальную — настройку для каждого элемента.

Ограничения и рекомендации

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

Таким образом, мы рассмотрели два основных подхода к получению элементов иерархии для сложных настроек. Динамический запрос проще в реализации для небольших объемов, а метод с временными таблицами и «плоским» развертыванием иерархии лучше подходит для масштабируемых систем с высокими требованиями к производительности.

← На главную