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

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

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

Анализ проблемы: почему не работает вложенный цикл?

В исходной ситуации разработчик использует три вложенных цикла для обхода дерева. Однако на практике часто случается, что выполнение «не заходит» в самый глубокий цикл (детальные записи). Проанализируем структуру запроса из примера:


|ВЫБРАТЬ
|   ...
|ИТОГИ ПО
|   МетодКонтроля,
|   Номенклатура

В данном запросе определено два уровня итогов. Это означает, что результат запроса имеет трехуровневую структуру:

  1. Первый уровень: Группировка по полю МетодКонтроля.
  2. Второй уровень: Группировка по полю Номенклатура внутри каждого метода контроля.
  3. Третий уровень: Детальные записи (сами строки документа, номера сертификатов и т.д.).

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

Решение №1: Последовательный обход через метод Выбрать()

Разберем по шагам, как правильно организовать вложенные циклы. Основное правило: каждый следующий уровень выборки должен вызываться от текущей строки родительской выборки.

Шаг 1: Получаем верхний уровень (МетодКонтроля). Мы используем параметр ОбходРезультатаЗапроса.ПоГруппировкам.

Шаг 2: Получаем средний уровень (Номенклатура). Здесь важно снова указать обход по группировкам, чтобы не «провалиться» сразу в детальные записи, пропустив итоги по номенклатуре.

Шаг 3: Получаем детальные записи. На самом нижнем уровне мы вызываем Выбрать() без параметров (по умолчанию это ОбходРезультатаЗапроса.Прямой).

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


РезультатЗапроса = Запрос.Выполнить();

// 1. Выборка первого уровня группировки
ВыборкаМетод = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаМетод.Следующий() Цикл
    ВеткаМетод = Дерево.Строки.Добавить();
    ВеткаМетод.НоменклатураПартия = ВыборкаМетод.МетодКонтроля;
    
    // 2. Выборка второго уровня группировки (ВАЖНО: тоже по группировкам)
    ВыборкаНоменклатура = ВыборкаМетод.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    Пока ВыборкаНоменклатура.Следующий() Цикл
        ВеткаНоменклатура = ВеткаМетод.Строки.Добавить();
        ВеткаНоменклатура.НоменклатураПартия = ВыборкаНоменклатура.Номенклатура;
        
        // 3. Выборка детальных записей
        ВыборкаДетальные = ВыборкаНоменклатура.Выбрать();
        Пока ВыборкаДетальные.Следующий() Цикл
            СтрокаДанных = ВеткаНоменклатура.Строки.Добавить();
            // Заполняем данные из детальной записи
            ЗаполнитьЗначенияСвойств(СтрокаДанных, ВыборкаДетальные);
            // Дополнительное форматирование строки для вывода
            СтрокаДанных.НоменклатураПартия = "Партия " + СокрЛП(ВыборкаДетальные.НомерСертификата);
        КонецЦикла;
    КонецЦикла;
КонецЦикла;

Решение №2: Использование метода Выгрузить() — самый быстрый способ

Проанализируем более эффективный подход. Платформа 1С предоставляет мощный инструмент автоматического построения дерева — метод Выгрузить(). Если в запросе корректно настроены ИТОГИ ПО, система сама создаст иерархическую структуру объекта ДеревоЗначений.

Рассмотрим, как это сделать в одну строку:


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

Преимущества этого метода:

Однако стоит учитывать особенность: при такой выгрузке в строках-группировках (верхние уровни) значения будут заполнены только для тех полей, которые указаны в предложении ИТОГИ ПО. Остальные поля будут содержать NULL или значения по умолчанию. Если вам нужно специфическое оформление текста (как в примере автора «Партия + №»), после выгрузки можно один раз рекурсивно обойти дерево для трансформации данных.

Полезные советы при работе с деревьями и запросами

Проанализируем несколько важных нюансов, которые помогут избежать ошибок в будущем:

  1. Использование ЗаполнитьЗначенияСвойств(): Чтобы не писать вручную присваивание каждой колонки (СтрокаДерева.Поле1 = Выборка.Поле1), используйте функцию ЗаполнитьЗначенияСвойств(СтрокаДерева, Выборка). Это сделает код чище и защитит от ошибок при переименовании полей в запросе.
  2. Обработка строк в запросе: Операции типа СтрЗаменить или Лев внутри цикла замедляют работу программы. Если есть возможность, старайтесь подготовить данные для вывода прямо в тексте запроса, используя строковые функции SQL (если это допустимо в вашей версии СУБД) или вычисляемые поля в СКД.
  3. Типы данных: При программном создании ДеревоЗначений всегда явно определяйте типы колонок. Если дерево будет связано с реквизитом формы, не забудьте вызвать ЗначениеВРеквизитФормы(ДеревоОбъект, "РеквизитДеревоНаФорме") в управляемом приложении.
  4. Пустые итоги: Помните, что если поле, по которому считаются итоги, содержит NULL, в дереве появится ветка с пустым значением. Это часто сбивает с толку пользователей. Рекомендуется использовать в запросе функцию ЕСТЬNULL(Поле, "Значение по умолчанию").

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

← На главную