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

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

Что такое итератор в 1С и как с ним работать

Итератор в 1С — механизм последовательного обхода данных. Разбираем выборку запроса, циклы по коллекциям, типичные ошибки и приёмы оптимизации кода.

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

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

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

Что такое итератор простыми словами

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

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

Цикл «Для Каждого» — основной итератор коллекций

Самый распространённый способ итерации в 1С — цикл Для Каждого Элемент Из Коллекция Цикл. Он работает с массивами, таблицами значений, списками значений, структурами, строками табличных частей и другими коллекциями.

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

Для Каждого СтрокаТовара Из ТаблицаТоваров Цикл

СуммаДокумента = СуммаДокумента + СтрокаТовара.Сумма;

КонецЦикла;

Альтернатива — классический цикл со счётчиком Для Индекс = 0 По Коллекция.Количество() - 1 Цикл. Он нужен, когда важен порядковый номер элемента или когда требуется обход в обратном порядке, например при удалении строк из таблицы значений во время обхода.

Выборка из результата запроса — итератор по данным базы

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

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

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

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

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

КонецЦикла;

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

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

Выборка или выгрузка: какой итератор выбрать

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

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

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

Подходит для больших результатов и однократного последовательного обхода. Экономит память, но не позволяет вернуться назад и обращаться по индексу.

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

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

Сводная выборка по группировкам

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

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

Обход иерархических данных и группировок

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

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

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

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

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

// обработка детальной записи

КонецЦикла;

КонецЦикла;

Для справочников и иерархических данных существуют также методы платформы ВыбратьИерархически() и Выбрать() у менеджеров объектов, но в большинстве задач предпочтительнее запрос: он выполняется на стороне сервера СУБД и не тянет лишние поля.

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

Типичные ошибки
01
Запрос внутри цикла: на каждой итерации выполняется новый запрос к базе, что резко замедляет обработку. Решение — соединить данные в одном запросе или использовать пакетный запрос с временными таблицами.
02
Изменение коллекции во время обхода «Для Каждого»: удаление или добавление элементов ломает логику итератора и приводит к пропускам.
03
Повторный обход исчерпанной выборки без Сбросить(): второй цикл просто не выполнится, и данные будут потеряны без сообщения об ошибке.
04
Использование Количество() у выборки как условие обхода вместо Следующий(): лишний код и путаница в логике.
05
Обращение к полям, которых нет в выборке: возникает ошибка времени выполнения, которую легко избежать проверкой текста запроса.

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

Как проверить и ускорить обход данных

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

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

Число обработанных записей
Норма

Совпадает с количеством записей в выборке или таблице

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

Меньше ожидаемого — часть записей пропущена из-за ошибки в цикле

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

Растёт примерно пропорционально объёму данных

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

Резко увеличивается при росте данных — вероятен запрос в цикле

Повторный запуск обработки
Норма

Даёт тот же результат или корректно пропускает обработанное

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

Дублирует изменения — нет контроля повторной обработки

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

FAQ: частые вопросы об итераторах в 1С

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

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

Чем отличается «Для Каждого» от цикла со счётчиком?

«Для Каждого» проще и безопаснее: платформа сама следит за границами коллекции. Цикл со счётчиком нужен, когда важен индекс элемента, требуется обход в обратном порядке или частичный обход с заданной позиции.

Почему запрос внутри цикла считается плохой практикой?

Каждый запрос — это обращение к СУБД с накладными расходами на компиляцию и передачу данных. Тысяча итераций означает тысячу обращений. Один запрос с соединением таблиц выполнит ту же работу за одно обращение и на порядки быстрее.

Как удалить строки таблицы значений во время обхода?

Обходите таблицу по индексу с конца к началу: тогда удаление текущей строки не сдвинет позиции ещё не проверенных элементов. Альтернатива — собрать строки на удаление в массив внутри «Для Каждого» и удалить их после цикла.

Что делать, если выборка возвращает иерархию справочника?

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

Есть ли в 1С аналог итераторов из других языков с методами HasNext/Next?

Явного интерфейса с такими методами нет, но пару «проверка + переход» выполняет метод Следующий() выборки, а для коллекций роль итератора берёт на себя сам цикл «Для Каждого», управляемый платформой.

Итог

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

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