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

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

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

Почему СКД вызывает функцию несколько раз?

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

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

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

Решение 1: Модуль с повторным использованием возвращаемых значений

Разберем самый популярный и универсальный подход. Чтобы не перегружать систему дублирующими вычислениями, мы можем завернуть нашу «тяжелую» функцию в специальный общий модуль. Для этого в свойствах общего модуля необходимо установить свойство Повторное использование возвращаемых значений в значение НаВремяВызова.

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

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

Решение 2: Перенос логики в язык запросов

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

Используйте левые соединения с виртуальными таблицами оборотов, временные таблицы и конструкцию ВЫБОР КОГДА. Это полностью снимает проблему двойных вызовов в СКД, так как СУБД самостоятельно и очень быстро рассчитает итоговый результат на этапе выполнения запроса.

Решение 3: Использование внешнего набора данных

Если алгоритм настолько сложен, что переложить его на запрос невозможно, рассмотрим вариант с внешним набором данных. В этом случае мы переносим логику в программный код (например, в модуль объекта отчета).

Посмотрим на примерный алгоритм действий:

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

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

Дополнительные настройки при программном выводе СКД

Если вы формируете отчет не типовыми кнопками, а используете собственный программный код для вывода результата СКД, вы можете столкнуться с ошибкой «Функция не найдена». Чтобы СКД могла использовать внешние функции из общих модулей, необходимо явно передать соответствующее разрешение при инициализации процессора компоновки данных.

Посмотрим на пример правильной инициализации:


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

Обратите внимание: общий модуль, к которому обращается СКД, должен быть серверным (должна стоять галочка ВызовСервера или Глобальный), а сама функция должна иметь директиву Экспорт.

Выводы и рекомендации

Мы рассмотрели несколько путей оптимизации. На практике старайтесь придерживаться следующего порядка действий:

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