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

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

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

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

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

Решение: программная подмена набора данных

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

Рассмотрим реализацию данного метода по шагам:

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

Пример программной реализации

Давайте посмотрим, как это выглядит в коде 1С:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    Настройки = КомпоновщикНастроек.ПолучитьНастройки();    
    
    ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
    
    НаборДанных = МакетКомпоновки.НаборыДанных.ВнутренниеДанные;
    
    // Получаем запрос из макета для учета пользовательских фильтров
    Запрос = Новый Запрос(НаборДанных.Запрос);
    Для Каждого ПараметрЗапроса Из МакетКомпоновки.ЗначенияПараметров Цикл
        Запрос.УстановитьПараметр(ПараметрЗапроса.Имя, ПараметрЗапроса.Значение);
    КонецЦикла;
    
    ТЗ = Запрос.Выполнить().Выгрузить();
    
    // Здесь мы один раз вызываем нашу сложную функцию для каждой строки
    ОбработкаРезультатаСКД(ТЗ);
    
    // Подмена набора данных в макете на объектный
    СвойстваНабора = Новый Структура("Имя,ИсточникДанных,Поля,Отбор");
    ЗаполнитьЗначенияСвойств(СвойстваНабора, НаборДанных);    
    МакетКомпоновки.НаборыДанных.Удалить(НаборДанных);
    
    НаборДанных = МакетКомпоновки.НаборыДанных.Добавить(Тип("НаборДанныхОбъектМакетаКомпоновкиДанных"));
    ЗаполнитьЗначенияСвойств(НаборДанных, СвойстваНабора);
    НаборДанных.ИмяОбъекта = "ТЗ";
    
    // Добавляем поля в новый набор
    Для Каждого Поле Из СвойстваНабора.Поля Цикл
        НовоеПоле = НаборДанных.Поля.Добавить();
        ЗаполнитьЗначенияСвойств(НовоеПоле, Поле);
    КонецЦикла;
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, Новый Структура("ТЗ", ТЗ), ДанныеРасшифровки, Истина);
    
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
КонецПроцедуры

Почему этот метод эффективен?

Используя данный подход, вы получаете полный контроль над процессом формирования отчета. Основные преимущества:

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

← На главную