Работа с иерархическими структурами данных в 1С — одна из базовых задач разработчика. Часто возникает ситуация, когда в запросе используются итоги (предложение ИТОГИ ПО), и эти данные необходимо корректно перенести в объект ДеревоЗначений для последующего отображения на форме или обработки. Рассмотрим типичные ошибки и правильные способы решения этой задачи.
В исходной ситуации разработчик использует три вложенных цикла для обхода дерева. Однако на практике часто случается, что выполнение «не заходит» в самый глубокий цикл (детальные записи). Проанализируем структуру запроса из примера:
|ВЫБРАТЬ
| ...
|ИТОГИ ПО
| МетодКонтроля,
| Номенклатура
В данном запросе определено два уровня итогов. Это означает, что результат запроса имеет трехуровневую структуру:
МетодКонтроля.Номенклатура внутри каждого метода контроля.Основная причина, по которой вложенные циклы могут работать некорректно, заключается в неправильном использовании метода Выбрать(). Если при получении выборки второго уровня не указать тип обхода, система может не понять, что вы хотите двигаться вглубь по группировкам.
Разберем по шагам, как правильно организовать вложенные циклы. Основное правило: каждый следующий уровень выборки должен вызываться от текущей строки родительской выборки.
Шаг 1: Получаем верхний уровень (МетодКонтроля). Мы используем параметр ОбходРезультатаЗапроса.ПоГруппировкам.
Шаг 2: Получаем средний уровень (Номенклатура). Здесь важно снова указать обход по группировкам, чтобы не «провалиться» сразу в детальные записи, пропустив итоги по номенклатуре.
Шаг 3: Получаем детальные записи. На самом нижнем уровне мы вызываем Выбрать() без параметров (по умолчанию это ОбходРезультатаЗапроса.Прямой).
Посмотрим на исправленный пример кода:
РезультатЗапроса = Запрос.Выполнить();
// 1. Выборка первого уровня группировки
ВыборкаМетод = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаМетод.Следующий() Цикл
ВеткаМетод = Дерево.Строки.Добавить();
ВеткаМетод.НоменклатураПартия = ВыборкаМетод.МетодКонтроля;
// 2. Выборка второго уровня группировки (ВАЖНО: тоже по группировкам)
ВыборкаНоменклатура = ВыборкаМетод.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаНоменклатура.Следующий() Цикл
ВеткаНоменклатура = ВеткаМетод.Строки.Добавить();
ВеткаНоменклатура.НоменклатураПартия = ВыборкаНоменклатура.Номенклатура;
// 3. Выборка детальных записей
ВыборкаДетальные = ВыборкаНоменклатура.Выбрать();
Пока ВыборкаДетальные.Следующий() Цикл
СтрокаДанных = ВеткаНоменклатура.Строки.Добавить();
// Заполняем данные из детальной записи
ЗаполнитьЗначенияСвойств(СтрокаДанных, ВыборкаДетальные);
// Дополнительное форматирование строки для вывода
СтрокаДанных.НоменклатураПартия = "Партия " + СокрЛП(ВыборкаДетальные.НомерСертификата);
КонецЦикла;
КонецЦикла;
КонецЦикла;
Проанализируем более эффективный подход. Платформа 1С предоставляет мощный инструмент автоматического построения дерева — метод Выгрузить(). Если в запросе корректно настроены ИТОГИ ПО, система сама создаст иерархическую структуру объекта ДеревоЗначений.
Рассмотрим, как это сделать в одну строку:
ДеревоЗначений = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
Преимущества этого метода:
Пока...Следующий().Однако стоит учитывать особенность: при такой выгрузке в строках-группировках (верхние уровни) значения будут заполнены только для тех полей, которые указаны в предложении ИТОГИ ПО. Остальные поля будут содержать NULL или значения по умолчанию. Если вам нужно специфическое оформление текста (как в примере автора «Партия + №»), после выгрузки можно один раз рекурсивно обойти дерево для трансформации данных.
Проанализируем несколько важных нюансов, которые помогут избежать ошибок в будущем:
СтрокаДерева.Поле1 = Выборка.Поле1), используйте функцию ЗаполнитьЗначенияСвойств(СтрокаДерева, Выборка). Это сделает код чище и защитит от ошибок при переименовании полей в запросе.СтрЗаменить или Лев внутри цикла замедляют работу программы. Если есть возможность, старайтесь подготовить данные для вывода прямо в тексте запроса, используя строковые функции SQL (если это допустимо в вашей версии СУБД) или вычисляемые поля в СКД.ДеревоЗначений всегда явно определяйте типы колонок. Если дерево будет связано с реквизитом формы, не забудьте вызвать ЗначениеВРеквизитФормы(ДеревоОбъект, "РеквизитДеревоНаФорме") в управляемом приложении.NULL, в дереве появится ветка с пустым значением. Это часто сбивает с толку пользователей. Рекомендуется использовать в запросе функцию ЕСТЬNULL(Поле, "Значение по умолчанию").Выяснив причину остановки обхода (отсутствие явного указания ПоГруппировкам во вложенных выборках), мы можем выбрать наиболее подходящий путь: либо детальный ручной контроль каждой ветки в циклах, либо быструю автоматическую выгрузку всего результата сразу.