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