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