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

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

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

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

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

Ключевые объекты здесь — Запрос, РезультатЗапроса и ВыборкаИзРезультатаЗапроса. Понимание того, как они связаны и чем отличается Выбрать() от Выгрузить(), избавляет от лишних циклов, ошибок с типами данных и проблем с производительностью.

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

Базовая схема: выполнение запроса и получение выборки

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

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

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

"ВЫБРАТЬ

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

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

|ИЗ

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

|ГДЕ

| НЕ Номенклатура.ПометкаУдаления";

Выборка = Запрос.Выполнить().Выбрать();

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

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

КонецЦикла;

Обращение к полям внутри цикла выполняется через точку по имени поля, заданному в секции ВЫБРАТЬ (или по псевдониму после КАК). Допустимо и обращение по индексу — Выборка[0], но по имени код читается надёжнее и не ломается при изменении порядка полей.

Метод Следующий() возвращает Истина, пока есть непрочитанные строки, и позиционирует выборку на очередную строку. До первого вызова Следующий() выборка стоит «перед» первой записью, поэтому обращаться к полям вне цикла нельзя — получите ошибку или пустые значения.

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

Выборка или таблица значений: что выбрать

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

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

Таблица значений нужна, если требуется сортировка (Сортировать()), поиск (Найти(), НайтиСтроки()), свёртка (Свернуть()), подсчёт итогов по колонке (Итог()) или повторный обход тех же данных. Перебирается таблица циклом Для Каждого Строка Из Таблица Цикл или по индексу.

Сравнение вариантов обхода

Выборка (Выбрать)

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

Таблица значений (Выгрузить)

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

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

Иерархический обход с уровнями. Подходит для отчётов с группами, но требует рекурсии или вложенных циклов.

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

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

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

"ВЫБРАТЬ

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

| СУММА(Продажи.СуммаОборот) КАК Сумма

|ИЗ

| РегистрНакопления.Продажи.Обороты(, , , ) КАК Продажи

|СГРУППИРОВАТЬ ПО

| Продажи.Номенклатура

|ИТОГИ СУММА(Сумма) ПО

| ОБЩИЕ";

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

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

Сообщить("Общий итог: " + ВыборкаИтоги.Сумма);

КонецЦикла;

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

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

Обращение к полям и типы данных

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

Правильный подход — вытащить все нужные поля в тексте запроса. Если нужен реквизит ссылки, добавьте его в выборку: Номенклатура.Код КАК Код. Тогда в цикле используется Выборка.Код без дополнительных обращений к базе.

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

Проверка результата и контроль пустоты

Перед обходом полезно проверить, есть ли вообще данные. Метод Пустой() объекта РезультатЗапроса возвращает Истина, если запрос не вернул ни одной строки. Это дешевле, чем запускать цикл и ждать первого Следующий(), и позволяет сразу вывести понятное сообщение пользователю.

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

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

Сообщить("Данные за период не найдены");

Возврат;

КонецЕсли;

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

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

// обработка строк

КонецЦикла;

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

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

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

Наличие данных
Норма

Результат.Пустой() = Ложь, цикл выполняется хотя бы раз

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

Пустой() = Истина — проверьте параметры и отборы запроса

Значения полей
Норма

Поля содержат ожидаемые типы: ссылки, числа, даты

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

В полях NULL или Неопределено — проверьте соединения и ЕСТЬNULL

Производительность
Норма

Один запрос, обход без обращений к базе внутри цикла

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

В цикле есть Ссылка.Реквизит или новые запросы — замедление на больших объёмах

Типичные ошибки при переборе запроса

Частая ошибка — обращение к полю, которого нет в выборке. Платформа выдаёт «Поле объекта не обнаружено». Причины: опечатка в имени, поле не добавлено в секцию ВЫБРАТЬ, либо конфликт имён при соединении нескольких таблиц — тогда помогают явные псевдонимы через КАК.

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

Третья группа — производительность: запрос в цикле, разыменование ссылок, выгрузка огромного результата в таблицу значений ради одного прохода, отсутствие индексированных отборов в параметрах виртуальных таблиц. На больших базах эти ошибки превращают секундную операцию в многоминутную.

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

Практические сценарии и рекомендации

Заполнение табличной части документа

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

Пакетная обработка с транзакцией

Когда в цикле изменяются объекты базы (записываются документы, движения), оборачивайте обход в транзакцию: НачатьТранзакцию() … ЗафиксироватьТранзакцию() с обработчиком ОтменитьТранзакцию() в исключении. Это гарантирует, что при сбое на середине не останется частично обработанных данных. Для очень больших объёмов фиксируйте транзакцию порциями, чтобы не держать длинные блокировки.

Когда запрос вообще не нужен

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

Маршрут решения

Как выбрать действие

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

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

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

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

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

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

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

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

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

Нет, выборка — однонаправленный курсор: после завершения цикла она исчерпана. Чтобы пройти данные повторно, выполните запрос заново или заранее выгрузите результат в таблицу значений и обходите её сколько угодно раз.

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

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

Почему в поле выборки NULL и как это исправить?

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

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

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