Работа с большими объемами данных в системе компоновки данных (СКД) часто ставит перед разработчиком непростую задачу. Когда количество строк в результате запроса достигает нескольких миллионов, стандартные механизмы вывода в табличный документ начинают потреблять колоссальный объем оперативной памяти, что приводит к зависанию сессии или аварийному завершению работы 1С с ошибкой «Недостаточно памяти». Разберем подробно, почему это происходит и как оптимизировать процесс выгрузки, чтобы обработка 2–2.5 млн строк стала стабильной и быстрой.
Проанализируем архитектуру процесса вывода. В 1С ТабличныйДокумент — это не просто текстовая сетка, а сложный объект. Каждая ячейка в нем обладает множеством свойств: оформление, шрифты, параметры расшифровки. Когда мы выводим 2 миллиона строк, платформа создает в памяти миллионы объектов. Если при этом включена расшифровка, система дополнительно хранит карту соответствий между ячейками и данными, что увеличивает потребление ресурсов в геометрической прогрессии. Даже на 64-битном сервере формирование такого отчета может занимать часы из-за нагрузки на менеджер памяти и сборщик мусора.
Первый и самый важный шаг при программном формировании тяжелых отчетов — полный отказ от механизмов расшифровки и сложного оформления. Рассмотрим, как правильно инициализировать ПроцессорКомпоновкиДанных. Обратите внимание на третий параметр метода Инициализировать — он отвечает за данные расшифровки. Если передать туда Неопределено или пустое значение и установить четвертый параметр (возможность использования внешних функций) в Ложь, потребление памяти существенно снизится.
Разберем пример кода для оптимизированной инициализации:
// Создаем схему и настройки
СхемаКомпоновкиДанных = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
// Формируем макет компоновки БЕЗ расшифровки
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
// Инициализируем процессор компоновки
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
// Третий параметр - НЕ передаем объект данных расшифровки
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , , Ложь);
Выясним причину, почему это помогает. Отсутствие расшифровки освобождает систему от необходимости отслеживать связи каждой ячейки с объектами базы данных. Если отчет предназначен для выгрузки в файл, расшифровка пользователю все равно не потребуется.
Если задача состоит в том, чтобы просто получить данные для последующей обработки, рассмотрим вариант вывода результата в ТаблицаЗначений. Это работает быстрее, чем формирование ТабличногоДокумента, так как в таблице значений отсутствуют визуальные свойства ячеек. Однако стоит помнить, что при 2 млн строк сама таблица в памяти также может занять значительный объем (около 2-4 ГБ в зависимости от количества колонок).
Для реализации этого метода используем ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений:
РезультатТЗ = Новый ТаблицаЗначений;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(РезультатТЗ);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Когда требуется максимально быстрая выгрузка (например, в CSV или текстовый файл), лучше всего использовать СКД только как конструктор запроса. Платформа 1С позволяет извлечь итоговый текст запроса со всеми примененными пользовательскими отборами и параметрами из уже скомпонованного макета. Это позволяет нам выполнить обычный Запрос и обойти его результат в цикле, записывая данные напрямую в текстовый файл с помощью объекта ЗаписьТекста. Посмотрим на этот алгоритм подробнее.
Рассмотрим, как извлечь запрос из макета компоновки:
// Получаем макет компоновки (как в первом примере)
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки);
// В макете компоновки для набора данных типа "Запрос" содержится готовый текст
ТекстЗапроса = МакетКомпоновки.НаборыДанных.НаборДанных1.Запрос;
// Создаем обычный объект Запрос
Запрос = Новый Запрос(ТекстЗапроса);
// Переносим параметры из макета компоновки в запрос
Для Каждого ПараметрМакеты Из МакетКомпоновки.ЗначенияПараметров Цикл
Запрос.УстановитьПараметр(ПараметрМакеты.Имя, ПараметрМакеты.Значение);
КонецЦикла;
// Выполняем запрос и обходим результат
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
ФайлВыгрузки = Новый ЗаписьТекста("C:\Export\data.csv", КодировкаТекста.UTF8);
Пока Выборка.Следующий() Цикл
// Формируем строку и записываем её
СтрокаДанных = Строка(Выборка.Код) + ";" + Выборка.Наименование;
ФайлВыгрузки.ЗаписатьСтроку(СтрокаДанных);
КонецЦикла;
ФайлВыгрузки.Закрыть();
Проанализируем преимущества данного подхода:
Если вы все же решили использовать ТабличныйДокумент для сохранения в TXT (как упоминалось в некоторых сообщениях форума), воспользуйтесь методом последовательного вывода. Вместо того чтобы вызывать ПроцессорВывода.Вывести(ПроцессорКомпоновки), который пытается обработать весь объем разом, используйте цикл с методом ВывестиЭлемент(). Это позволит системе более эффективно управлять памятью в процессе формирования документа.
Также обратите внимание на Пользовательские поля. Если внутри СКД вы используете сложные выражения в пользовательских полях (например, конкатенацию множества строк с форматированием), это замедляет расчет каждой строки. При выгрузке 2 миллионов строк такие затраты времени становятся критичными. По возможности перенесите логику формирования строк непосредственно в текст запроса или обрабатывайте их при записи в файл.
Таким образом, для работы с миллионными выгрузками в СКД лучшей стратегией является использование схемы как генератора запроса с последующей потоковой записью данных в файл, минуя создание тяжелых визуальных объектов платформы.