Как дополнить таблицу значений элементами, входящими в иерархию выбранных групп?

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

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

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

Способ 1: Использование массива параметров и условия В ИЕРАРХИИ

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

Сначала подготовим массив уникальных групп из нашей таблицы значений. Затем передадим его в запрос к справочнику. Главная хитрость здесь — сопоставить полученные элементы с их исходными группами-"родителями", чтобы проставить верное "Качество". Поскольку 1С не возвращает, по какой именно группе из массива был найден элемент в условии В ИЕРАРХИИ, нам придется либо делать это в цикле, либо использовать дополнительные механизмы.

Рассмотрим пример кода, где мы получаем все вложенные элементы:


// 1. Получаем массив уникальных групп из исходной ТЗ
МассивГрупп = ИсходнаяТЗ.ВыгрузитьКолонку("Номенклатура");

// 2. Формируем запрос для получения всех подчиненных элементов
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Номенклатура.Ссылка КАК Ссылка,
    |   Номенклатура.Родитель КАК Родитель
    |ИЗ
    |   Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |   Номенклатура.Ссылка В ИЕРАРХИИ(&МассивГрупп)
    |   И НЕ Номенклатура.ЭтоГруппа";

Запрос.УстановитьПараметр("МассивГрупп", МассивГрупп);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();

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

Способ 2: Использование Системы компоновки данных (СКД)

Система компоновки данных — это, пожалуй, самый мощный и элегантный способ обойти ограничение на соединение по иерархии. В СКД реализован специальный механизм "Связи наборов данных", который поддерживает флаг "Иерархическая связь".

Разберем алгоритм настройки этого механизма:

  1. Создадим программно схему компоновки данных.
  2. Добавим Набор данных - объект (наша исходная ТаблицаЗначений). Назовем его НаборТЗ.
  3. Добавим Набор данных - запрос к справочнику Номенклатура. Назовем его НаборСправочник.
  4. В связях наборов данных укажем:
    • Источник связи: НаборТЗ, поле Номенклатура.
    • Приемник связи: НаборСправочник, параметр (или поле) для фильтрации.
    • Установим галочку "В иерархии".
  5. Выгрузим результат компоновки в новую ТаблицуЗначений.

Этот метод позволяет системе автоматически выполнить рекурсивный поиск для каждой строки вашей таблицы и привязать найденные элементы к соответствующим значениям "Качества" из той же строки.

Способ 3: Итерационный метод обхода (Снизу вверх или Сверху вниз)

Если справочник огромный, а использование В ИЕРАРХИИ приводит к падению производительности (SQL Server может генерировать тяжелые планы запросов для этого оператора), мы можем применить метод последовательного получения уровней. Выясним причину эффективности этого метода: он использует индекс по полю Родитель.

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


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

Такой подход гарантирует контроль над нагрузкой и позволяет обрабатывать иерархию любой глубины без риска переполнения стека запроса.

Способ 4: Динамическое построение запроса (Схема запроса)

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

Для каждой строки исходной таблицы в текст запроса добавляется блок:


ВЫБРАТЬ Номенклатура.Ссылка, &Качество1 КАК Качество 
ИЗ Справочник.Номенклатура КАК Номенклатура 
ГДЕ Номенклатура.Ссылка В ИЕРАРХИИ(&Группа1)

Проанализируем ситуацию: этот метод хорош тем, что за один пакетный запрос мы получаем сразу готовую таблицу с проставленным качеством для каждого вложенного элемента. Однако помните об ограничении на количество объединений в одном запросе (обычно не рекомендуется более 256–512 UNION).

Рекомендации по оптимизации

При работе с иерархическими данными в 1С важно придерживаться следующих правил:

Выбор конкретного метода зависит от ваших условий. Если важна простота кода — используйте Массив + В ИЕРАРХИИ. Если нужна максимальная производительность и корректное сопоставление данных — выбирайте СКД или Итерационный обход.

← На главную