Как устранить многократный вызов функций вычисляемых полей в отчетах СКД?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов на базе системы компоновки данных (СКД) разработчики часто сталкиваются с ситуацией, когда функции общих модулей, прописанные в выражениях вычисляемых полей, вызываются платформой по несколько раз для одной и той же строки данных. При увеличении количества вычисляемых полей и ресурсов число повторных обращений к функциям растет в геометрической прогрессии (для двух полей — 4 раза, для трех — 8 раз и так далее). В результате формирование отчета существенно замедляется.

Разберем детально, почему платформа 1С выполняет избыточные вычисления, и рассмотрим практические способы решения этой проблемы.

Почему СКД выполняет функции вычисляемых полей многократно

Проанализируем механику работы процессора компоновки данных. Движок СКД выполняет расчет результирующего набора данных в несколько этапов:

  1. Многоэтапный расчет и агрегация: когда вычисляемое поле добавляется на вкладку Ресурсы, платформа вычисляет его значение на этапе получения детальных записей, затем при группировке и расчете итогов. При наличии нескольких ресурсов СКД выполняет перекрестный обход комбинаций агрегируемых колонок.
  2. Отсутствие информации о детерминированности: для процессора компоновки функция общего модуля является внешним кодом. СКД не может заранее знать, зависит ли результат функции исключительно от переданных параметров или же меняется при каждом вызове. Поэтому платформа вызывает функцию заново при любом обращении к полю.
  3. Обработка условного оформления и отборов: если поле или зависимый от него ресурс участвует в пользовательских фильтрах или правилах оформления, СКД может повторно пересчитывать выражение при выводе каждой отдельной ячейки макета.

Способ 1. Использование модуля с повторным использованием возвращаемых значений

Рассмотрим самый быстрый способ устранить просадку производительности без кардинальной переработки структуры отчета. Если вызываемая функция является чистой (возвращает одинаковый результат при одинаковых входных параметрах), кэширование полностью исключит лишние расчеты.

  1. Создадим отдельный неглобальный общий модуль, например, ОтчетыКэшПереопределяемый.
  2. В свойствах модуля установим флаг Сервер, а также свойство Повторное использование возвращаемых значений в значение На время вызова.
  3. Поместим функцию расчета в этот модуль с ключевым словом Экспорт.

Посмотрим на пример реализации функции расчета:


Функция РассчитатьКоэффициент(Знач НоменклатураСсылка, Знач ПериодРасчета) Экспорт
    
    // Выполняем требуемый расчет
    Результат = 0;
    
    // Пример вычисления логики
    Если ЗначениеЗаполнено(НоменклатураСсылка) Тогда
        Результат = НоменклатураСсылка.СтавкаНДС.Ставка / 100;
    КонецЕсли;
    
    Возврат Результат;
    
КонецФункции

В выражении вычисляемого поля СКД укажем вызов с именем модуля: ОтчетыКэшПереопределяемый.РассчитатьКоэффициент(Номенклатура, Период).

Обратите внимание: кэширование эффективно в том случае, если параметры функции часто повторяются. Если аргументом передается уникальный идентификатор для каждой строки таблицы из сотен тысяч записей, кэш займет значительный объем оперативной памяти на сервере до завершения компоновки.

Способ 2. Перенос вычислений на вкладку «Ресурсы»

Выясним, действительно ли требуется вычислять значение на уровне детальных записей. Если результат функции необходим только в группировках или итоговых строках, не следует указывать вызов функции на вкладке Вычисляемые поля.

  1. Оставим вкладку Вычисляемые поля пустой для данного показателя или удалим из нее вызов внешнего модуля.
  2. Перейдем на вкладку Ресурсы и добавим нужное поле.
  3. В колонке Выражение напишем вызов функции, например: ОтчетыСервер.РассчитатьИтог(Сумма(СуммаДокумента)).
  4. В колонке Рассчитывать по... укажем только те группировки, для которых действительно требуется расчет, исключив детальные записи.

Способ 3. Перенос логики в текст запроса (SQL)

Наиболее производительным архитектурным решением является полный отказ от вызова функций 1С внутри СКД в пользу языка запросов. Все математические преобразования, строковые манипуляции и проверки условий гораздо быстрее выполняет СУБД.

Разберем пример замены внешней функции конструкцией ВЫБОР КОГДА внутри набора данных-запроса:


ВЫБРАТЬ
    Продажи.Номенклатура КАК Номенклатура,
    Продажи.Количество КАК Количество,
    Продажи.Сумма КАК Сумма,
    ВЫБОР
        КОГДА Продажи.Количество > 100 ТОГДА Продажи.Сумма * 0.15
        КОГДА Продажи.Количество > 50 ТОГДА Продажи.Сумма * 0.10
        ИНАЧЕ 0
    КОНЕЦ КАК СуммаСкидки
ИЗ
    РегистрНакопления.Продажи КАК Продажи

Такой подход полностью снимает нагрузку с процессора компоновки данных и исключает многократные пересчеты интерпретатором 1С.

Способ 4. Пакетная подготовка данных через «Набор данных - Объект»

Если алгоритм расчета слишком сложен для языка запросов и требует работы с прикладными объектами, сформируем данные предварительно в коде модуля отчета с помощью пакетной обработки.

  1. Получим исходные данные запросом в ТаблицаЗначений.
  2. Выполним все вычисления пакетно в цикле или специализированными алгоритмами, заполнив расчетные колонки.
  3. Передадим готовую таблицу в СКД через структуру ВнешниеНаборыДанных.

Посмотрим на пример компоновки с внешним набором данных:


СхемаКомпоновкиДанных = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
Настройки = СхемаКомпоновкиДанных.НастройкиПоУмолчанию;

ВнешниеНаборы = Новый Структура;
ВнешниеНаборы.Вставить("ТаблицаДанных", ПодготовленнаяТаблица);

КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборы, ДанныеРасшифровки);

ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

При таком подходе СКД работает с уже рассчитанными значениями как с простыми полями, не вызывая повторных процедур расчета строк.

← На главную