К статье
Практический материал

Разработка и Конфигурация · 10 мин

Как в 1С обойти результат запроса по группировкам

Как в 1С выполнить обход результата запроса по группировкам: метод Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам), итоги, вложенные выборки, типичные ошибки и примеры кода.

Обход результата запроса по группировкам — стандартный приём в 1С, когда нужно получить данные в виде иерархии: сначала итог по группе, затем детальные записи внутри неё. Типичные задачи — печатные формы с шапкой и табличной частью, отчёты с промежуточными итогами, выгрузки, где строки сгруппированы по контрагенту, складу или документу.

Механизм реализуется через метод Выбрать() результата запроса с параметром ОбходРезультатаЗапроса.ПоГруппировкам. Ключевой момент: группировки должны быть описаны в самом запросе через секцию ИТОГИ ... ПО — без неё обход по группировкам невозможен, и это самая частая причина ошибок у начинающих разработчиков.

Ниже разобран полный рабочий пример, вложенные выборки, обход по группировкам с иерархией, проверка результата и типичные ошибки с их последствиями.

Как устроен обход по группировкам в 1С

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

Метод Выбрать() принимает два важных параметра: способ обхода (ОбходРезультатаЗапроса.ПоГруппировкам или ПоГруппировкамСИерархией) и имя группировки, если их несколько. Если группировка одна, имя можно не указывать.

Базовый пример: группа и детальные записи

Классическая задача — вывести продажи по контрагентам: сначала итог по контрагенту, затем его документы. Запрос и обход выглядят так:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Продажи.Контрагент КАК Контрагент,

| Продажи.Документ КАК Документ,

| Продажи.Сумма КАК Сумма

|ИЗ

| РегистрНакопления.Продажи КАК Продажи

|ИТОГИ

| СУММА(Сумма)

|ПО

| Контрагент";

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

ВыборкаКонтрагенты = Результат.Выбрать(

ОбходРезультатаЗапроса.ПоГруппировкам);

Пока ВыборкаКонтрагенты.Следующий() Цикл

// Итог по контрагенту

Сообщить(ВыборкаКонтрагенты.Контрагент

+ ": " + ВыборкаКонтрагенты.Сумма);

// Детальные записи внутри группы

ВыборкаДетали = ВыборкаКонтрагенты.Выбрать();

Пока ВыборкаДетали.Следующий() Цикл

Сообщить(" " + ВыборкаДетали.Документ

+ " — " + ВыборкаДетали.Сумма);

КонецЦикла;

КонецЦикла;

Обратите внимание: в выборке группы поле Сумма содержит итог по группе (потому что в секции ИТОГИ указана агрегатная функция СУММА), а в детальной выборке — значение конкретной строки. Внутренний Выбрать() без параметров возвращает именно детальные записи текущей группы.

Порядок действий
  1. 1
    Напишите запрос и добавьте секцию ИТОГИ с агрегатными функциями и полями группировки
  2. 2
    Выполните запрос и получите результат через Запрос.Выполнить()
  3. 3
    Создайте выборку: Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам)
  4. 4
    В цикле Пока Следующий() обработайте итог группы
  5. 5
    Внутри цикла вызовите ВыборкаГруппы.Выбрать() и обойдите детальные записи
  6. 6
    Проверьте итоги: сумма по группе должна совпадать с суммой деталей

Несколько уровней группировок

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

ВыборкаСклады = Результат.Выбрать(

ОбходРезультатаЗапроса.ПоГруппировкам, "Склад");

Пока ВыборкаСклады.Следующий() Цикл

ВыборкаНоменклатура = ВыборкаСклады.Выбрать(

ОбходРезультатаЗапроса.ПоГруппировкам, "Номенклатура");

Пока ВыборкаНоменклатура.Следующий() Цикл

ВыборкаДетали = ВыборкаНоменклатура.Выбрать();

Пока ВыборкаДетали.Следующий() Цикл

// обработка детальной записи

КонецЦикла;

КонецЦикла;

КонецЦикла;

Порядок группировок в секции ИТОГИ определяет порядок вложенности: первая группировка — внешний цикл. Если перепутать уровни, логика отчёта «поедет»: итоги будут считаться не по тем разрезам, хотя запрос выполнится без ошибки.

Обход по группировкам с иерархией

Когда группировочное поле — иерархический справочник (например, номенклатура с папками), и нужно обойти и папки, и элементы, используется способ ОбходРезультатаЗапроса.ПоГруппировкамСИерархией. В этом режиме выборка содержит дополнительные строки для родительских элементов.

Определить тип текущей строки выборки помогает свойство ТипЗаписи(): оно возвращает значения вида «ИтогПоГруппировке», «ИтогПоИерархии», «ДетальнаяЗапись». Проверка типа записи позволяет выводить папки одним оформлением, а элементы — другим, и не смешивать итоги с деталями.

Как проверить, что обход работает правильно

Самая надёжная проверка — сверка итогов. Сумма по группе из выборки верхнего уровня должна совпадать с суммой детальных записей этой группы, а общий итог (если задан ИТОГИ ... ПО ОБЩИЕ) — с суммой всех групп. Расхождение обычно означает, что часть деталей теряется из-за соединения таблиц или отбора.

Вторая проверка — количество строк. Временно добавьте в цикл счётчики и сравните число обработанных детальных записей с результатом того же запроса без секции ИТОГИ. Если счётчики не сходятся, ищите проблему в соединениях (например, ЛЕВОЕ СОЕДИНЕНИЕ задублировало строки) или в условиях ГДЕ.

Контрольные точки

Как проверить результат

Итог группы
Норма

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

Тревожный признак

Итог больше или меньше суммы деталей — проверяйте соединения и отборы

Число строк
Норма

Количество детальных записей совпадает с запросом без ИТОГИ

Тревожный признак

Строк больше — вероятно дублирование из-за соединения по неуникальному ключу

Пустые группы
Норма

Пустые итоги обрабатываются или осознанно пропускаются

Тревожный признак

На группе с NULL в поле обход падает с ошибкой обращения к полю

Типичные ошибки и как их избежать

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

Вторая частая проблема — обращение к полю детальной записи из выборки группы. Если поле не входит в группировку и не описано агрегатной функцией в секции ИТОГИ, в выборке группы его значение будет NULL. Нужно либо добавить поле в ИТОГИ (например, МАКСИМУМ(Дата)), либо читать его только в детальном цикле.

Третья ошибка — попытка использовать обход по группировкам там, где достаточно линейной выборки. Если иерархия не нужна и итоги можно посчитать свёрткой таблицы значений, секция ИТОГИ только усложнит код и замедлит выполнение на больших объёмах.

Типичные ошибки
01
Вызов Выбрать(ПоГруппировкам) без секции ИТОГИ в запросе — цикл молча не выполняется
02
Чтение негруппировочного поля из выборки группы — получаете NULL вместо значения
03
Перепутанный порядок группировок в секции ИТОГИ — итоги считаются не по тем разрезам
04
Вложенный Выбрать() вызван у результата запроса, а не у выборки группы — детали берутся от всей выборки
05
Использование ПоГруппировкамСИерархией для неиерархического поля — лишние строки и ошибки логики

Альтернативы обходу по группировкам

Не всегда нужен именно обход по группировкам. Если задача — отчёт для пользователя, СКД (система компоновки данных) решает её декларативно: группировки и итоги настраиваются без кода. Если нужна программная обработка без иерархии, проще выгрузить результат в таблицу значений (Результат.Выгрузить()) и использовать Свернуть() или сортировку с контролем смены группы.

Обход по группировкам оправдан, когда нужна именно иерархическая обработка в коде: печатная форма с шапками групп, выгрузка XML с вложенными узлами, заполнение дерева значений на форме. В этих сценариях он даёт самый читаемый и управляемый код.

Сравнение вариантов

Обход по группировкам (ИТОГИ + Выбрать)

Подходит для иерархической обработки в коде: печатные формы, выгрузки, деревья. Требует аккуратности с вложенными выборками.

СКД

Подходит для отчётов пользователю: группировки и итоги настраиваются без кода. Не подходит для произвольной программной обработки.

Выгрузить() + Свернуть()

Подходит для плоской обработки и подсчёта итогов без иерархии. Не даёт вложенной структуры «группа — детали».

Итог

Что важно запомнить

Обход по группировкам требует секции ИТОГИ в запросе. Внешняя выборка создаётся с ОбходРезультатаЗапроса.ПоГруппировкам, детали — вложенным Выбрать() из выборки группы. Проверяйте результат сверкой итогов группы с суммой детальных записей.

Частые вопросы

Почему цикл по выборке по группировкам не выполняется ни разу?

Чаще всего в запросе нет секции ИТОГИ — без неё выборка по группировкам пуста. Проверьте также, что запрос вообще возвращает строки: выполните его в консоли запросов и убедитесь, что отборы не отсекают все данные.

Как получить общий итог по всем группам?

Добавьте в секцию ИТОГИ группировку ОБЩИЕ: «ИТОГИ СУММА(Сумма) ПО ОБЩИЕ, Контрагент». Первая запись выборки по группировкам будет общим итогом, её можно опознать по значению NULL в группировочном поле или по типу записи.

Можно ли обойти только группы без детальных записей?

Да. Просто не вызывайте вложенный Выбрать() — обойдите только внешнюю выборку по группировкам. Если детали вообще не нужны, рассмотрите вариант с СГРУППИРОВАТЬ ПО вместо ИТОГИ: запрос вернёт сразу по одной строке на группу, и обход будет линейным.

Чем ИТОГИ отличается от СГРУППИРОВАТЬ ПО?

СГРУППИРОВАТЬ ПО сворачивает данные до одной строки на группу — детали теряются. ИТОГИ сохраняет и детальные записи, и итоговые строки, строя иерархию. Выбор зависит от того, нужны ли вам детали внутри групп.

Как обойти выборку, если группировок несколько и нужны не все уровни?

Укажите имя нужной группировки вторым параметром: Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Номенклатура"). Платформа вернёт выборку по этому уровню независимо от того, сколько группировок объявлено в запросе.

Влияет ли обход по группировкам на производительность?

Сама выборка работает быстро, но секция ИТОГИ увеличивает объём данных, который строит и хранит платформа. На больших таблицах, если иерархия не нужна, линейная выборка или СГРУППИРОВАТЬ ПО будут экономичнее по памяти и времени.