При разработке алгоритмов выгрузки данных, в частности при экспорте информации в Excel или сторонние форматы, разработчики часто сталкиваются с ошибкой «Итератор для значения не определен». Проанализируем, почему возникает данное исключение, как устроен обход данных в платформе 1С:Предприятие и как грамотно организовать перебор строк и колонок.
В платформе 1С цикл Для Каждого ... Из ... Цикл предназначен исключительно для объектов-коллекций, поддерживающих интерфейс перечисления (например, массив, структура, соответствие, таблица значений, дерево значений, коллекция колонок или строк). Рассмотрим типичные ошибочные конструкции, приводящие к сбою:
Запрос или ВыборкаИзРезультатаЗапроса: Конструкция вида Для Каждого Стр Из Запрос Цикл вызывает исключение, поскольку сам объект Запрос не содержит в себе данных и не является коллекцией.Запрос.Ссылка или попытка обратиться по индексу через квадратные скобки к объекту выборки или запроса Запрос["ИмяПоля"] недопустимы.Запрос.Выполнить().Выбрать().Следующий() возвращает булево значение (Истина/Ложь), а не саму выборку или строку данных.Если требуется последовательно прочитать данные запроса с минимальным расходом оперативной памяти, используем объект ВыборкаИзРезультатаЗапроса. Обход выборки выполняется с помощью цикла Пока ... Цикл и вызова метода Следующий().
Разберем пример корректного кода для обхода данных выборки:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Сотрудники.Ссылка КАК Сотрудник,
| Сотрудники.Код КАК Код,
| Сотрудники.ТекущееПодразделение КАК Подразделение
|ИЗ
| Справочник.Сотрудники КАК Сотрудники";
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Обращение к полям осуществляется через точку по имени поля
ЗначениеСотрудник = Выборка.Сотрудник;
ЗначениеКод = Выборка.Код;
КонецЦикла;
Обратите внимание: при работе с выборкой обращение к полям осуществляется через точку по имени колонки, заданной в тексте запроса (например, Выборка.Сотрудник).
Когда стоит задача динамически перебирать имена колонок (например, через строковые переменные СтрокаДанных[ИмяКолонки]) или обходить строки с помощью конструкции Для Каждого, результат запроса необходимо предварительно выгрузить в Таблицу Значений.
Посмотрим на практический пример обхода строк и колонок таблицы:
РезультатЗапроса = Запрос.Выполнить();
ТаблицаДанных = РезультатЗапроса.Выгрузить();
// 1. Формируем заголовки колонок в файле Excel
КолонкаЛиста = 1;
Для Каждого Колонка Из ТаблицаДанных.Колонки Цикл
Лист.Cells(1, КолонкаЛиста).Value = Колонка.Имя;
КолонкаЛиста = КолонкаЛиста + 1;
КонецЦикла;
// 2. Построчно выгружаем данные
СтрокаЛиста = 2;
Для Каждого СтрокаТаблицы Из ТаблицаДанных Цикл
КолонкаЛиста = 1;
Для Каждого Колонка Из ТаблицаДанных.Колонки Цикл
// Доступ к значению по имени колонки через квадратные скобки
ЗначениеЯчейки = СтрокаТаблицы[Колонка.Имя];
Если ТипЗнч(ЗначениеЯчейки) = Тип("Число") Тогда
Лист.Cells(СтрокаЛиста, КолонкаЛиста).NumberFormat = "0,00";
Лист.Cells(СтрокаЛиста, КолонкаЛиста).Value = Формат(ЗначениеЯчейки, "ЧГ=0");
Иначе
Лист.Cells(СтрокаЛиста, КолонкаЛиста).NumberFormat = "@";
Лист.Cells(СтрокаЛиста, КолонкаЛиста).Value = Строка(ЗначениеЯчейки);
КонецЕсли;
КолонкаЛиста = КолонкаЛиста + 1;
КонецЦикла;
СтрокаЛиста = СтрокаЛиста + 1;
КонецЦикла;
Поячеечная запись данных через COM-объект Excel (Лист.Cells(Row, Col).Value = ...) работает крайне медленно на больших объемах информации и зависит от установленного пакета MS Office на рабочем месте. Разберем более быстрый и универсальный способ формирования Excel-файла с помощью встроенного Табличного Документа.
Рассмотрим пример формирования файла через табличный документ:
ТабДок = Новый ТабличныйДокумент;
// Заполняем табличный документ данными из выборки или таблицы
// ... (вывод областей макета)
// Сохраняем результат в формате XLSX напрямую средствами платформы
ТабДок.Записать(ПолноеИмяФайла, ТипФайлаТабличногоДокумента.XLSX);
Если же бизнес-логика требует использования COM-соединения, всегда очищайте память и закрывайте приложение после завершения работы:
Книга.SaveAs(ПолноеИмяФайла);
ПриложениеExcel.Quit();
// Освобождаем COM-объекты для исключения зависания процесса EXCEL.EXE
Лист = Неопределено;
Книга = Неопределено;
ПриложениеExcel = Неопределено;