При работе с большими объемами данных (сотни тысяч записей) разработчики часто сталкиваются с ситуацией, когда выборка из базы выполняется мгновенно, но итоговое построение отчета или вывод данных в табличный документ занимает неприемлемо много времени или приводит к зависанию сеанса. Проанализируем типичную ситуацию: отчет строится по регистру сведений на 600 000+ строк, и как только в выборку добавляются ссылки на документы, скорость катастрофически падает из-за логики, заложенной в модуль менеджера этих объектов.
Выясним причину такого поведения платформы 1С и разберем практические способы оптимизации.
Разберем детально, что происходит «под капотом» платформы 1С при выводе ссылочных типов данных в отчет:
ОбработкаПолученияПредставления, но отсутствует парная процедура ОбработкаПолученияПолейПредставления, платформа не может выполнить пакетное чтение полей. В результате для каждой из 600 000 строк генерируется отдельное обращение к базе данных.ОбработкаПолученияПредставления выполняются прямые запросы к СУБД или обращения к реквизитам через точку (например, Данные.Организация.Наименование), система выполняет миллионы неявных запросов к базе.ТабличныйДокумент компоновщик макета инициализирует структуру расшифровки для каждой ячейки, что многократно увеличивает время отрисовки и потребление оперативной памяти процессом rphost.Самый эффективный способ исключить влияние модулей менеджеров на производительность отчета — отказаться от вывода ссылочного типа и сформировать строковое представление непосредственно в теле запроса СУБД. Посмотрим на пример:
ВЫБРАТЬ
Регистр.Период КАК Период,
Регистр.Номенклатура КАК Номенклатура,
// Получаем строковое представление стандартным механизмом СУБД:
ПРЕДСТАВЛЕНИЕ(Регистр.ДокументДвижения) КАК ДокументПредставление,
// Либо формируем строку из простых полей документа:
"№ " + Документ.Номер + " от " + ФОРМАТ(Документ.Дата, "ДФ=dd.MM.yyyy") КАК ДокументСтрока
ИЗ
РегистрСведений.ИсторияСтатусов КАК Регистр
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ИзменениеСтатуса КАК Документ
ПО Регистр.ДокументДвижения = Документ.Ссылка
При таком подходе платформа не инициализирует события модуля менеджера, а сервер СУБД выполняет соединение и форматирование строк на порядки быстрее.
Если отчет строится с использованием схемы компоновки данных (СКД), мы можем переопределить стандартный механизм получения представления прямо на вкладке «Наборы данных».
Разберем по шагам, как это настроить:
ДокументДвижения)."№ " + ДокументДвижения.Номер или имя заранее подготовленного в запросе строкового поля ДокументПредставление.Если изменить отчет невозможно или логику формирования представлений необходимо сохранить для всей конфигурации, приведем код модуля менеджера к стандартам разработки 1С. Рассмотрим правильную реализацию обоих обработчиков:
// В модуле менеджера документа:
Процедура ОбработкаПолученияПолейПредставления(Поля, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Указываем только простые реквизиты, необходимые для формирования строки
Поля.Добавить("Номер");
Поля.Добавить("Дата");
Поля.Добавить("ВидОперации");
КонецПроцедуры
Процедура ОбработкаПолученияПредставления(Данные, Представление, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Формируем строку исключительно из переданной структуры Данные,
// без выполнения запросов и обращений через точку
Представление = СтрШаблон("Документ № %1 от %2 (%3)",
Данные.Номер,
Формат(Данные.Дата, "ДФ=dd.MM.yyyy"),
Данные.ВидОперации);
КонецПроцедуры
Важный момент: объявление процедуры ОбработкаПолученияПолейПредставления позволяет платформе одним запросом получить значения указанных реквизитов для всей пачки ссылок, что устраняет проблему зависания.
Если конечной целью построения отчета на 600 000+ строк является получение данных во внешнем формате (например, Excel или CSV), вывод такого массива в экранный ТабличныйДокумент является архитектурной ошибкой. Это гарантированно вызовет пиковое потребление RAM и риск аварийного завершения рабочего процесса сервера.
Для таких задач используем потоковую запись выборки запроса с помощью объекта ЗаписьТекста:
Запись = Новый ЗаписьТекста("C:\Export\Data.csv", КодировкаТекста.UTF8);
// Записываем заголовок
Запись.ЗаписатьСтроку("Номенклатура;Документ;Дата");
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
СтрокаДанных = СтрШаблон("%1;%2;%3",
Выборка.НоменклатураНаименование,
Выборка.ДокументПредставление,
Формат(Выборка.Период, "ДФ=dd.MM.yyyy"));
Запись.ЗаписатьСтроку(СтрокаДанных);
КонецЦикла;
Запись.Закрыть();
Потоковая запись потребляет фиксированный минимальный объем оперативной памяти независимо от того, сколько строк выгружается — 10 000 или 1 000 000.