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