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

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

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

Обход результата запроса в 1С: цикл по выборке, обход по группировкам, выгрузка в таблицу значений и дерево. Примеры кода, ошибки и рекомендации по производительности.

Результат запроса в 1С — это не набор строк, к которым можно обратиться напрямую по индексу, а объект типа «РезультатЗапроса», из которого данные извлекают специальными способами. Чаще всего используют выборку через метод Выполнить().Выбрать() и цикл Пока Выборка.Следующий() Цикл, но это не единственный вариант: для отчётов удобнее выгрузка в таблицу значений, а для иерархических данных — обход по группировкам или дерево значений.

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

Как устроен результат запроса и что с ним можно делать

Метод Выполнить() объекта Запрос возвращает РезультатЗапроса. У него есть три ключевых действия: проверить пустоту методом Пустой(), получить выборку методом Выбрать() и выгрузить данные методом Выгрузить(). Дополнительно метод Выбрать() принимает параметр типа обхода, который определяет, как будут упорядочены и сгруппированы строки.

Важно понимать, что выборка — это однонаправленный курсор: пройти её можно только один раз, вернуться к предыдущей строке нельзя. Если данные нужны повторно, либо получайте выборку заново, либо сразу выгружайте результат в таблицу значений.

Базовый обход выборкой в цикле

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

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

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

"ВЫБРАТЬ

| Номенклатура.Ссылка КАК Ссылка,

| Номенклатура.Наименование КАК Наименование

|ИЗ

| Справочник.Номенклатура КАК Номенклатура";

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

Если Не РезультатЗапроса.Пустой() Тогда

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

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

Сообщить(Выборка.Наименование);

КонецЦикла;

КонецЕсли;

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

Если в запросе используются псевдонимы полей, обращаться к полям выборки нужно именно по псевдонимам. При отсутствии псевдонима имя поля формируется платформой, и угадывать его не стоит — всегда задавайте псевдонимы явно через КАК.

Обход по группировкам и итогам

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

ВыборкаПоГруппам = РезультатЗапроса.Выбрать(

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

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

// строка итога по группировке

Сообщить("Группа: " + ВыборкаПоГруппам.Контрагент

+ ", сумма: " + ВыборкаПоГруппам.Сумма);

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

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

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

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

КонецЦикла;

КонецЦикла;

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

Выгрузка в таблицу значений и дерево

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

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

Для Каждого СтрокаТаблицы Из ТаблицаРезультата Цикл

Сообщить(СтрокаТаблицы.Наименование);

КонецЦикла;

// произвольный доступ по индексу

ПерваяСтрока = ТаблицаРезультата[0];

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

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

Выборка + цикл Следующий()

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

Выгрузка в таблицу значений

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

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

Сохраняет структуру итогов запроса с ИТОГИ ПО. Ограничение: требует аккуратной работы с вложенными выборками.

Выгрузка в дерево значений

Готова к выводу иерархии на форму. Ограничение: обход сложнее, чем у плоской таблицы.

Обход с записью данных: типичный сценарий

Частая задача — прочитать результат запроса и на его основе изменить объекты базы: установить цены, заполнить регистр, обновить реквизиты. Здесь важно не смешивать чтение и запись бездумно: если внутри цикла по выборке выполняется запись большого числа объектов, разумно обернуть обработку в транзакцию либо накапливать данные и записывать пакетно.

Порядок действий
  1. 1
    Сформируйте запрос так, чтобы он возвращал только нужные поля и строки — с отбором через ГДЕ и параметрами
  2. 2
    Проверьте результат методом Пустой() и обработайте случай отсутствия данных
  3. 3
    Получите выборку методом Выбрать() и обойдите её циклом Пока Выборка.Следующий() Цикл
  4. 4
    Внутри цикла работайте со строкой через псевдонимы полей; при записи объектов используйте транзакцию
  5. 5
    После цикла проверьте количество обработанных строк и зафиксируйте результат в журнале или сообщении

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

Производительность и частые ошибки

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

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

Типичные ошибки
01
Обращение Выборка.Ссылка.Реквизит внутри цикла — каждое обращение через точку от ссылки выполняет отдельное чтение из базы; добавьте нужные поля в текст запроса
02
Запрос внутри цикла по выборке — количество обращений к СУБД растёт пропорционально числу строк; используйте соединения и временные таблицы
03
Повторный обход той же выборки — курсор уже в конце, цикл не выполнится; получайте выборку заново или выгружайте в таблицу значений
04
Обращение к полю по имени без псевдонима в запросе — имя колонки может не совпасть с ожиданием; всегда задавайте псевдонимы через КАК
05
Выгрузка огромного результата в таблицу значений ради одного прохода — лишний расход памяти; используйте выборку
Контрольные точки

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

Количество обработанных строк
Норма

Счётчик в цикле совпадает с ожидаемым числом строк результата

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

Обработано меньше строк, чем ожидалось — проверьте отборы и соединения в запросе

Время выполнения
Норма

Обход выполняется за предсказуемое время без роста при увеличении данных

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

Время растёт непропорционально — вероятны запросы или чтения объектов внутри цикла

Данные после обхода
Норма

Все изменения записаны, контрольные значения совпадают с выборкой

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

Часть объектов не изменена — проверяйте обработку исключений и транзакции

Частые вопросы по обходу результата запроса

Чем отличается Выбрать() от Выгрузить()?

Выбрать() возвращает курсор-выборку для однократного последовательного чтения с минимальным расходом памяти. Выгрузить() помещает весь результат в таблицу значений в памяти, зато позволяет обращаться к строкам по индексу, искать и сортировать. Для больших данных и однократного прохода выбирайте выборку, для многократной работы — выгрузку.

Как узнать количество строк результата запроса?

У выборки нет свойства с количеством строк. Варианты: выгрузить результат в таблицу значений и взять её Количество(), либо добавить в запрос вычисление числа строк агрегатной функцией КОЛИЧЕСТВО, либо посчитать строки счётчиком в цикле обхода.

Можно ли обойти выборку дважды?

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

Как обойти результат с иерархией справочника?

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

Почему поле выборки возвращает NULL?

NULL появляется при левых соединениях, когда в присоединяемой таблице нет подходящей строки, или при итоговых строках группировок. Проверяйте значение функцией ЗначениеЗаполнено() или сравнением с Неопределено/NULL, а в запросе используйте ЕСТЬNULL() для подстановки значения по умолчанию.

Что быстрее: обход выборки или таблицы значений?

Для однократного последовательного чтения выборка обычно экономнее по памяти и не требует промежуточного размещения данных. Таблица значений выигрывает, когда нужен поиск, сортировка или повторные проходы. Разница становится заметной на десятках тысяч строк и более.

Итог

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

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