Рассмотрим частую ситуацию при разработке отчетов в 1С с использованием Системы Компоновки Данных (СКД). Вы добавляете вычисляемое поле, в колонке «Выражение» прописываете вызов функции из общего модуля (например, для сложного расчета текущего процента скидки по карте лояльности). Однако при формировании отчета выясняется, что эта функция вызывается многократно для одной и той же строки данных. Проанализируем причину такого поведения и разберем по шагам все возможные способы решения этой проблемы.
Многократный вызов функций из общих модулей для одной строки — это известная архитектурная особенность СКД. Выясним причину: вычисление происходит не только для формирования самого значения поля, но и для множества других внутренних процессов компоновки. СКД может вызывать вашу функцию дополнительно в следующих случаях:
Если для поля задать только «Выражение представления», то значение самого поля будет пустым, и мы не сможем установить по нему отбор. Поэтому нам необходимо сохранить вызов функции в основном выражении, но при этом оптимизировать производительность.
Разберем самый популярный и универсальный подход. Чтобы не перегружать систему дублирующими вычислениями, мы можем завернуть нашу «тяжелую» функцию в специальный общий модуль. Для этого в свойствах общего модуля необходимо установить свойство Повторное использование возвращаемых значений в значение НаВремяВызова.
Как это работает? При первом обращении к функции для конкретной строки платформа выполнит вычисление и поместит результат в кэш. Все последующие обращения от СКД (для итогов, представлений, отборов) будут мгновенно получать готовый результат из кэша. Использование варианта НаВремяВызова оптимально, так как оно не засоряет память сервера надолго и автоматически очищает кэш после завершения формирования отчета.
Важное условие: чтобы кэширование отработало корректно, передавайте в функцию абсолютно все параметры, влияющие на результат. Если результат зависит от накопленного объема продаж, сегмента партнера и других данных, все они должны быть переданы как аргументы функции. Если функция будет брать данные из глобального контекста внутри себя, кэш может вернуть неверное значение для разных строк.
Проанализируем ситуацию с точки зрения архитектуры. Если логика расчета скидки опирается на данные базы (например, продажи из регистров, вхождения в сегменты), лучшим решением будет вообще отказаться от вычисляемого поля с внешней функцией. Перепишем эту логику на язык запросов внутри самого набора данных СКД.
Используйте левые соединения с виртуальными таблицами оборотов, временные таблицы и конструкцию ВЫБОР КОГДА. Это полностью снимает проблему двойных вызовов в СКД, так как СУБД самостоятельно и очень быстро рассчитает итоговый результат на этапе выполнения запроса.
Если алгоритм настолько сложен, что переложить его на запрос невозможно, рассмотрим вариант с внешним набором данных. В этом случае мы переносим логику в программный код (например, в модуль объекта отчета).
Посмотрим на примерный алгоритм действий:
НаборДанныхОбъект).У этого метода есть минус: для сохранения оптимальной работы отчета вам придется программно считывать пользовательские настройки отборов из компоновщика настроек и транслировать их на ваш исходный запрос, собирающий данные для таблицы значений, что усложняет разработку.
Если вы формируете отчет не типовыми кнопками, а используете собственный программный код для вывода результата СКД, вы можете столкнуться с ошибкой «Функция не найдена». Чтобы СКД могла использовать внешние функции из общих модулей, необходимо явно передать соответствующее разрешение при инициализации процессора компоновки данных.
Посмотрим на пример правильной инициализации:
// Четвертый параметр (Истина) разрешает использование внешних функций
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, , , Истина);
Обратите внимание: общий модуль, к которому обращается СКД, должен быть серверным (должна стоять галочка ВызовСервера или Глобальный), а сама функция должна иметь директиву Экспорт.
Мы рассмотрели несколько путей оптимизации. На практике старайтесь придерживаться следующего порядка действий:
ВычислитьВыражение, математические формулы). Внутренние функции работают значительно быстрее вызовов серверного модуля.НаВремяВызова, не забывая передавать в нее все нужные параметры.