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