При разработке отчетов на базе системы компоновки данных (СКД) разработчики часто сталкиваются с ситуацией, когда функции общих модулей, прописанные в выражениях вычисляемых полей, вызываются платформой по несколько раз для одной и той же строки данных. При увеличении количества вычисляемых полей и ресурсов число повторных обращений к функциям растет в геометрической прогрессии (для двух полей — 4 раза, для трех — 8 раз и так далее). В результате формирование отчета существенно замедляется.
Разберем детально, почему платформа 1С выполняет избыточные вычисления, и рассмотрим практические способы решения этой проблемы.
Проанализируем механику работы процессора компоновки данных. Движок СКД выполняет расчет результирующего набора данных в несколько этапов:
Рассмотрим самый быстрый способ устранить просадку производительности без кардинальной переработки структуры отчета. Если вызываемая функция является чистой (возвращает одинаковый результат при одинаковых входных параметрах), кэширование полностью исключит лишние расчеты.
ОтчетыКэшПереопределяемый.На время вызова.Экспорт.Посмотрим на пример реализации функции расчета:
Функция РассчитатьКоэффициент(Знач НоменклатураСсылка, Знач ПериодРасчета) Экспорт
// Выполняем требуемый расчет
Результат = 0;
// Пример вычисления логики
Если ЗначениеЗаполнено(НоменклатураСсылка) Тогда
Результат = НоменклатураСсылка.СтавкаНДС.Ставка / 100;
КонецЕсли;
Возврат Результат;
КонецФункции
В выражении вычисляемого поля СКД укажем вызов с именем модуля: ОтчетыКэшПереопределяемый.РассчитатьКоэффициент(Номенклатура, Период).
Обратите внимание: кэширование эффективно в том случае, если параметры функции часто повторяются. Если аргументом передается уникальный идентификатор для каждой строки таблицы из сотен тысяч записей, кэш займет значительный объем оперативной памяти на сервере до завершения компоновки.
Выясним, действительно ли требуется вычислять значение на уровне детальных записей. Если результат функции необходим только в группировках или итоговых строках, не следует указывать вызов функции на вкладке Вычисляемые поля.
ОтчетыСервер.РассчитатьИтог(Сумма(СуммаДокумента)).Наиболее производительным архитектурным решением является полный отказ от вызова функций 1С внутри СКД в пользу языка запросов. Все математические преобразования, строковые манипуляции и проверки условий гораздо быстрее выполняет СУБД.
Разберем пример замены внешней функции конструкцией ВЫБОР КОГДА внутри набора данных-запроса:
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
Продажи.Количество КАК Количество,
Продажи.Сумма КАК Сумма,
ВЫБОР
КОГДА Продажи.Количество > 100 ТОГДА Продажи.Сумма * 0.15
КОГДА Продажи.Количество > 50 ТОГДА Продажи.Сумма * 0.10
ИНАЧЕ 0
КОНЕЦ КАК СуммаСкидки
ИЗ
РегистрНакопления.Продажи КАК Продажи
Такой подход полностью снимает нагрузку с процессора компоновки данных и исключает многократные пересчеты интерпретатором 1С.
Если алгоритм расчета слишком сложен для языка запросов и требует работы с прикладными объектами, сформируем данные предварительно в коде модуля отчета с помощью пакетной обработки.
ТаблицаЗначений.ВнешниеНаборыДанных.Посмотрим на пример компоновки с внешним набором данных:
СхемаКомпоновкиДанных = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
Настройки = СхемаКомпоновкиДанных.НастройкиПоУмолчанию;
ВнешниеНаборы = Новый Структура;
ВнешниеНаборы.Вставить("ТаблицаДанных", ПодготовленнаяТаблица);
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборы, ДанныеРасшифровки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
При таком подходе СКД работает с уже рассчитанными значениями как с простыми полями, не вызывая повторных процедур расчета строк.