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

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

В практике разработки на платформе 1С часто возникает задача получения настроек или параметров, которые могут быть заданы как для конкретного элемента справочника, так и для его родительских групп. Типичный пример — «Ограничение скидки». Если для конкретного товара ограничение не установлено, система должна «подняться» выше по дереву иерархии и найти значение у ближайшего родителя. Сложность возрастает, когда нужно получить такие данные не для одного товара, а для массива из 20–30 тысяч позиций при неограниченной вложенности групп.

Рассмотрим подробнее несколько подходов к решению этой задачи: от простых (но ограниченных) до максимально производительных.

Вариант 1. Традиционный метод «точек» в запросе

Проанализируем ситуацию, когда глубина вложенности заранее известна или ограничена разумным пределом (например, до 5–10 уровней). В этом случае можно использовать объединение нескольких запросов. Каждый последующий блок ОБЪЕДИНИТЬ ВСЕ будет обращаться к родителям следующего уровня.

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


ВЫБРАТЬ ПЕРВЫЕ 1
    ОграничениеСкидки КАК Значение
ИЗ
    РегистрСведений.ОграниченияСкидок КАК Рег
ГДЕ
    Рег.Номенклатура = &Номенклатура

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ ПЕРВЫЕ 1
    ОграничениеСкидки
ИЗ
    РегистрСведений.ОграниченияСкидок КАК Рег
ГДЕ
    Рег.Номенклатура = &Номенклатура.Родитель

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ ПЕРВЫЕ 1
    ОграничениеСкидки
ИЗ
    РегистрСведений.ОграниченияСкидок КАК Рег
ГДЕ
    Рег.Номенклатура = &Номенклатура.Родитель.Родитель

Важные моменты этого метода:

  1. Метод крайне громоздкий при глубокой иерархии. Если вложенность составит 20–50 уровней, текст запроса станет нечитаемым.
  2. Использование конструкции Рег.Номенклатура.Родитель.Родитель порождает неявные левые соединения (Left Join) в SQL-запросе. При обработке массива в 30 000 элементов это может привести к значительной деградации производительности и даже падению рабочего процесса rphost.
  3. Для больших массивов этот метод использовать не рекомендуется.

Вариант 2. Итерационный поиск через код и временные таблицы

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

Проанализируем алгоритм:

  1. Помещаем исходный массив номенклатуры во временную таблицу (ВТ).
  2. Создаем таблицу «Результаты», куда будем записывать найденные значения.
  3. В цикле Пока получаем данные из регистра для тех элементов, которые еще не найдены в «Результатах».
  4. Для оставшихся элементов обновляем ВТ, заменяя ссылки на их родителей.
  5. Цикл прерывается, когда в ВТ остаются только пустые ссылки или когда все значения найдены.

Посмотрим на пример логики в коде:


// Шаг 1: Подготовка временной таблицы с исходными данными
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка ПОМЕСТИТЬ ВТ_Остаток ИЗ Справочник.Номенклатура ГДЕ Ссылка В (&Массив)");

Пока Истина Цикл
    // Шаг 2: Пытаемся получить значения для текущего уровня
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    ВТ.Ссылка КАК ИсходнаяНоменклатура,
    |    Рег.ОграничениеСкидки
    |ИЗ
    |    ВТ_Остаток КАК ВТ
    |    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ОграниченияСкидок КАК Рег
    |    ПО ВТ.Ссылка = Рег.Номенклатура";
    
    Результат = Запрос.Выполнить().Выгрузить();
    // (Здесь логика сохранения найденных данных и удаления их из ВТ_Остаток)

    // Шаг 3: Переход на уровень выше
    Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |    ВТ.Ссылка.Родитель КАК Ссылка
    |ПОМЕСТИТЬ ВТ_Остаток_Новая
    |ИЗ ВТ_Остаток КАК ВТ
    |ГДЕ ВТ.Ссылка.Родитель <> ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)";
    
    // Если ВТ_Остаток_Новая пуста - прерываем цикл
КонецЦикла;

Этот метод гораздо стабильнее, так как он не перегружает SQL сложными соединениями, а работает итеративно с индексированными таблицами.

Вариант 3. Использование вспомогательного регистра (Closure Table)

Для задач, где критически важна скорость получения данных (например, расчет скидок в чеке в режиме реального времени), лучшим решением будет создание служебного регистра сведений. Его часто называют «Таблицей связей» или Closure Table.

Выясним причину эффективности этого метода: мы заранее (в фоновом режиме или при записи номенклатуры) просчитываем все связи «Узел — Предок».

Структура служебного регистра «ИерархияНоменклатуры»:

Теперь запрос для получения ограничения скидки становится тривиальным и очень быстрым:


ВЫБРАТЬ
    Иерархия.Узел КАК Номенклатура,
    МИНИМУМ(Иерархия.Дистанция) КАК МинДистанция
ПОМЕСТИТЬ ВТ_Ближайшие
ИЗ
    РегистрСведений.ИерархияНоменклатуры КАК Иерархия
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ОграниченияСкидок КАК Рег
    ПО Иерархия.Предок = Рег.Номенклатура
ГДЕ
    Иерархия.Узел В (&МассивНоменклатуры)
СГРУППИРОВАТЬ ПО
    Иерархия.Узел;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Номенклатура,
    Рег.ОграничениеСкидки
ИЗ
    ВТ_Ближайшие КАК ВТ
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ИерархияНоменклатуры КАК Иерархия
    ПО ВТ.Номенклатура = Иерархия.Узел И ВТ.МинДистанция = Иерархия.Дистанция
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ОграниченияСкидок КАК Рег
    ПО Иерархия.Предок = Рег.Номенклатура

Преимущества:

Вариант 4. Денормализация при записи (Кэширование)

Рассмотрим еще один практический совет. Если настройки меняются редко, а читаются часто, можно пойти по пути избыточности данных. При записи значения в регистр «Ограничение скидки» для группы, мы можем запускать фоновое задание, которое пропишет это же значение всем дочерним элементам в отдельный плоский регистр «АктуальныеОграничения».

Таким образом, при запросе нам не нужно будет знать ничего об иерархии — мы просто соединяемся с регистром по конкретной ссылке Номенклатура. Это самый быстрый способ с точки зрения чтения, но он требует тщательной проработки подписок на события ПриЗаписи справочника и регистра сведений, чтобы поддерживать данные в актуальном состоянии.

Выводы и рекомендации

Выбор метода зависит от ваших условий:

Использование индексации по измерениям регистра и полю Родитель справочника является обязательным условием для любого из выбранных методов.

← На главную