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