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