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

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

1С: как сбросить выборку запроса и обойти её повторно

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

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

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

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

Что такое выборка и почему она «заканчивается»

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

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

Синтаксис метода Сбросить()

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

Запрос = Новый Запрос("ВЫБРАТЬ Номенклатура, Количество ИЗ РегистрНакопления.Остатки.Остатки");

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

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

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

// первый обход

КонецЦикла;

Выборка.Сбросить(); // указатель снова в начале

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

// повторный обход тех же данных

КонецЦикла;

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

bsl
Выборка.Сбросить(); // полный сброс в начало
Выборка.Сбросить("Номенклатура"); // сброс в пределах текущей группы группировки

Повторный обход выборки: типовой сценарий

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

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

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

Сброс выборки с группировками

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

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

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

Альтернативы: когда Сбросить не нужен

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

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

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

Выборка + Сбросить()

Минимальный расход памяти, подходит для 2–3 проходов по большому результату; нет произвольного доступа к строкам

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

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

Временная таблица

Повторное использование в последующих запросах пакета; данные остаются на стороне СУБД до закрытия менеджера

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

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

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

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

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

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

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

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

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

Счётчик итераций второго цикла
Норма

Равен счётчику первого цикла

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

Ноль или меньше — сброшена не та выборка

Первая запись после сброса
Норма

Совпадает с первой записью первого обхода

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

Возвращается запись из середины или другой группы

Внешний цикл по группировкам
Норма

Продолжает обход групп в исходном порядке после сброса внутренней выборки

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

Группы пропускаются или повторяются

FAQ

Можно ли сбросить выборку, полученную методом Выгрузить()?

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

Работает ли Сбросить() для выборки по группировкам с иерархией?

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

Что быстрее: Сбросить() или повторное выполнение запроса?

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

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

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

Можно ли перейти к конкретной записи выборки по номеру?

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

Нужно ли сбрасывать выборку перед завершением процедуры?

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

Итог

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

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