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

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

При построении иерархических отчетов или заполнении форм часто возникает необходимость выгрузить результат запроса 1С в ДеревоЗначений. Однако при работе с секцией ИТОГИ ПО разработчики сталкиваются с ситуациями, когда нижние группы иерархии «терются» или, наоборот, группы начинаю дублировать сами себя на нижних уровнях. Проанализируем причины такого поведения и разберем способы решения данной задачи.

Причины проблемы при выгрузке иерархии

Рассмотрим, как платформа 1С обрабатывает иерархические итоги в запросе:

  1. Использование ключевого слова ТОЛЬКО ИЕРАРХИЯ: Данный режим формирует исключительно промежуточные родительские узлы. Если группа справочника является конечной в поддереве текущей выборки (у нее нет подчинённых элементов среди выбранных), платформа не считает ее родителем и исключает из итоговой структуры. В результате конечные категории исчезают из иерархии.
  2. Использование ключевого слова ИЕРАРХИЯ: В этом случае запрос формирует два типа записей для каждого элемента — итоговую запись иерархии и детальную запись самого элемента. При стандартном выгрузке в дерево значений методом Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией) это приводит к эффекту «вложенности в себя»: группа отображается как родительский узел и повторно как строка внутри самой себя.

Способ 1. Использование упорядочивания без секции ИТОГИ

Если в запросе не требуется вычислять числовые агрегатные функции (суммы, количества), можно отказаться от конструкции ИТОГИ ПО. Для этого применим предложение УПОРЯДОЧИТЬ ПО ... ИЕРАРХИЯ.

Разберем пример запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	СправочникНоменклатура.Ссылка КАК Категория,
    |	ЛОЖЬ КАК Выгружать
    |ИЗ
    |	Справочник.Номенклатура КАК СправочникНоменклатура
    |ГДЕ
    |	СправочникНоменклатура.ЭтоГруппа = ИСТИНА
    |	И СправочникНоменклатура.Ссылка В ИЕРАРХИИ(&ВерхняяГруппа)
    |	И СправочникНоменклатура.Ссылка <> &ВерхняяГруппа
    |УПОРЯДОЧИТЬ ПО
    |	Категория ИЕРАРХИЯ";

Запрос.УстановитьПараметр("ВерхняяГруппа", ВерхняяГруппа);

РезультатЗапроса = Запрос.Выполнить();
ДеревоЗначений = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);

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

Способ 2. Программная постобработка дерева значений

Если запрос формируется с использованием ИТОГИ ПО Категория ИЕРАРХИЯ, выгруженное дерево будет содержать задвоенные узлы (родитель включает строку с самим собой). Для очистки такой структуры выполним рекурсивный обход и удаление лишних элементов.

Рассмотрим алгоритм рекурсивного обхода дерева с формированием массива удаляемых строк:


Процедура ОчиститьДеревоОтДублей(ДеревоРезультат)
    
    МассивУдаляемыхСтрок = Новый Массив;
    ОбходДереваРекурсивно(ДеревоРезультат, Неопределено, МассивУдаляемыхСтрок);
    
    // Удаляем помеченные строки с конца или через сохраненную ссылку на родительский коллектор
    Для Каждого ЭлементМассива Из МассивУдаляемыхСтрок Цикл
        ЭлементМассива.ДеревоРодитель.Строки.Удалить(ЭлементМассива.СтрокаУдаления);
    КонецЦикла;

КонецПроцедуры

Процедура ОбходДереваРекурсивно(ТекущееДерево, УдаляемыйРодитель, МассивУдаляемыхСтрок)
    
    Для Каждого ТекСтрока Из ТекущееДерево.Строки Цикл
        
        Если УдаляемыйРодитель = ТекСтрока.Категория Тогда
            // Фиксируем дублирующую строку для последующего удаления
            СтруктураУдаления = Новый Структура;
            СтруктураУдаления.Вставить("ДеревоРодитель", ТекущееДерево);
            СтруктураУдаления.Вставить("СтрокаУдаления", ТекСтрока);
            МассивУдаляемыхСтрок.Добавить(СтруктураУдаления);
        Иначе
            ТекСтрока.Выгружать = ЛОЖЬ;
            
            Если ТекСтрока.Строки.Количество() > 0 Тогда
                ЛокальныйРодитель = ТекСтрока.Категория;
                ОбходДереваРекурсивно(ТекСтрока, ЛокальныйРодитель, МассивУдаляемыхСтрок);
            КонецЕсли;
        КонецЕсли;
        
    КонецЦикла;
    
КонецПроцедуры

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

Резюме

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

  1. Для простого построения дерева групп без вычисления итоговых сумм используйте УПОРЯДОЧИТЬ ПО ... ИЕРАРХИЯ.
  2. При использовании секции ИТОГИ ПО ... ТОЛЬКО ИЕРАРХИЯ помните, что конечные группы без подчинённых элементов будут отсекаться.
  3. При выгрузке итогов с предложением ИЕРАРХИЯ удаляйте дублирующие детальные строки рекурсивным обходом полученного объекта ДеревоЗначений.
← На главную