При разработке и доработке аналитических отчетов на базе объекта ПостроительОтчета часто возникает задача пакетного формирования данных. Например, требуется разбить единый результат на несколько независимых табличных документов по группам проектов или подразделений и отобразить каждый из них на отдельной вкладке панели. Разберем особенности реализации такой задачи, проанализируем возможные ошибки и рассмотрим оптимальные варианты программного кода.
Рассмотрим типовую ситуацию: у нас есть иерархический справочник проектов и типовой отчет, использующий ПостроительОтчета. Требуется обойти выбранные группы проектов и по каждой из них сформировать отдельный табличный документ.
Частая ошибка при реализации такой логики — выполнение лишнего запроса для получения подчиненных элементов группы:
Для каждого Сп Из СписокПроектовОтбор Цикл
СписокГруппПроектов = Новый СписокЗначений;
ГруппаПроектов = Сп.Значение;
ЗапросПроектыГРуппы = Новый Запрос;
ЗапросПроектыГРуппы.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
| Проекты.Ссылка КАК Пр
|ИЗ
| Справочник.Проекты КАК Проекты
|ГДЕ
| Проекты.Родитель = &Группа";
ЗапросПроектыГРуппы.УстановитьПараметр("Группа", ГруппаПроектов);
ВыборкаЭлПроектов = ЗапросПроектыГРуппы.Выполнить().Выбрать();
Пока ВыборкаЭлПроектов.Следующий() Цикл
СписокГруппПроектов.Добавить(ВыборкаЭлПроектов.Пр);
КонецЦикла;
СформироватьОтчет(НовПоле, СписокГруппПроектов);
КонецЦикла;
Выясним причину, почему такой подход неэффективен:
Проекты.Родитель = &Группа выбирает только непосредственных потомков первого уровня и пропускает элементы из более глубоких подгрупп.ВидСравнения.ВИерархии и ВидСравнения.ВСпискеПоИерархии. Нет необходимости предварительно выбирать подчиненные элементы запросом.Если мы не хотим модифицировать типовой текст запроса и переписывать процедуры формирования отчета, наиболее безопасным и быстрым способом является динамическая установка отбора в объекте ПостроительОтчета перед каждым вызовом формирования.
Разберем по шагам, как корректно реализовать этот механизм:
ПолучитьНастройки(). Это позволит восстановить фильтры и не затереть пользовательские параметры.ПостроительОтчета.Отбор. Если поле присутствует в запросе в блоке {ГДЕ ...}, мы можем безопасно обратиться к нему или добавить через метод Добавить().ВидСравнения.ВИерархии и передаем ссылку на группу справочника.УстановитьНастройки().Посмотрим на пример практической реализации в коде обработки:
Процедура СформироватьОтчетПоГруппам()
// Сохраняем исходные настройки построителя
ИсходныеНастройки = ПостроительОтчета.ПолучитьНастройки();
Для Каждого ЭлементСписка Из СписокГрупп Цикл
ТекущаяГруппа = ЭлементСписка.Значение;
// 1. Создаем страницу панели и поле табличного документа
ИмяВкладки = "Стр_" + СокрЛП(ТекущаяГруппа.УникальныйИдентификатор());
НоваяСтраница = ЭлементыФормы.ОсновнаяПанель.Страницы.Добавить(ИмяВкладки, СокрЛП(ТекущаяГруппа));
ИмяПоля = "ПолеТабДок_" + СокрЛП(ТекущаяГруппа.УникальныйИдентификатор());
НовоеПолеТабДок = ЭлементыФормы.Добавить(Тип("ПолеТабличногоДокумента"), ИмяПоля, Истина, ЭлементыФормы.ОсновнаяПанель);
НовоеПолеТабДок.Страница = НоваяСтраница;
// 2. Настраиваем отбор построителя отчета
ЭлементОтбора = ПостроительОтчета.Отбор.Найти("Проект");
Если ЭлементОтбора = Неопределено Тогда
ЭлементОтбора = ПостроительОтчета.Отбор.Добавить("Проект");
КонецЕсли;
ЭлементОтбора.Использование = Истина;
ЭлементОтбора.ВидСравнения = ВидСравнения.ВИерархии;
ЭлементОтбора.Значение = ТекущаяГруппа;
// 3. Выполняем построитель и выводим результат в созданный табличный документ
ПостроительОтчета.Выполнить();
НовоеПолеТабДок.Очистить();
ПостроительОтчета.Вывести(НовоеПолеТабДок);
КонецЦикла;
// Восстанавливаем первоначальные настройки отчета
ПостроительОтчета.УстановитьНастройки(ИсходныеНастройки);
КонецПроцедуры
Важный момент: динамическое добавление поля в ПостроительОтчета.Отбор сработает без ошибок только тогда, когда в тексте запроса построителя отчета описано соответствующее поле (например, в секции {ГДЕ Проекты.Ссылка.* КАК Проект} или в полях выборки).
Проанализируем ситуацию, при которой групп проектов достаточно много. Многократное выполнение ПостроительОтчета.Выполнить() в цикле создает высокую нагрузку на базу данных из-за повторных запросов.
В этом случае эффективнее выполнить запрос один раз со списком всех групп, сгруппировать данные по родительским проектам и разделить результат при обходе группировок:
ВидСравнения.ВСпискеПоИерархии.ПостроительОтчета.Выполнить().РезультатЗапроса обходим по группировкам верхнего уровня через Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам) и программно выводим секции макета в соответствующие поля табличных документов для каждой группы.Такой подход обеспечивает максимальную производительность при формировании отчетов по большому количеству объектов аналитики.