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

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

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

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

Зачем нужна программная подмена набора данных?

Стандартный путь формирования отчета подразумевает, что ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент инициирует выполнение запросов, описанных в схеме. Если мы сначала выгрузим данные в коллекцию через один процессор вывода, а затем попробуем вывести их в документ через другой — база данных получит два идентичных тяжелых запроса. Чтобы этого избежать, программисты прибегают к следующей хитрости: подменяют исходный НаборДанныхЗапрос на НаборДанныхОбъект уже после того, как данные получены.

Пошаговый алгоритм реализации механизма

Разберем базовый алгоритм, который позволяет выполнить запрос один раз и передать результат обратно в СКД:

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

Проанализируем пример кода для реализации этого алгоритма:


// 1. Получаем настройки и макет
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);

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

// 3. Подменяем схему (упрощенно)
СхемаКопия = СхемаКомпоновкиДанных; // В реальности лучше использовать СериализаторXDTO для глубокого копирования
НаборОбъект = СхемаКопия.НаборыДанных.НаборДанных1; // Допустим, он там один
// Здесь происходит магия замены типа в структуре объектов (см. ниже решение проблемы)

Проблема «невидимых» полей отбора

Выясним причину основной проблемы: если пользователь установил отбор по полю, которое не выбрано для отображения в отчете (например, отбор по Складу есть, а колонки «Склад» в отчете нет), то СКД при генерации итогового запроса в макете может не включить это поле в секцию ВЫБРАТЬ. Как следствие, в нашей РезультатТЗ этой колонки не будет.

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

Решение: Использование набора-«пустышки»

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

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

  1. Проанализируем структуру полей исходного НаборДанныхЗапрос.
  2. Для каждого поля, которое присутствует в схеме, но отсутствует в результирующей ТЗ, добавим описание в наш новый НаборДанныхОбъект.
  3. Чтобы избежать ошибок о несвязанных наборах данных, мы можем программно добавить эти поля в тот же самый набор данных, который мы передаем (в коллекцию Поля объекта НаборДанныхОбъект).

Посмотрим на пример программной модификации полей набора данных:


// Допустим, Схема2 — это копия нашей СКД
НаборОбъект = Схема2.НаборыДанных.Найти("ОсновнойНаборДанных");

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

Нюанс с пользовательскими настройками и параметрами

Часто разработчики сталкиваются с тем, что параметры или отборы «пропадают» при повторном формировании. Это происходит из-за того, что в СКД настройки разделены на Основные и Пользовательские. При программном выводе всегда используйте метод КомпоновщикНастроек.ПолучитьНастройки(). Он возвращает результирующий объект настроек, где все значения из формы уже применены к основной схеме.

Также обратите внимание на свойство Автозаполнение. Для наборов данных типа «Объект» автоматическое определение полей работает по-другому. Рекомендуется явно описывать коллекцию полей в схеме, копируя их из исходного запроса. Это гарантирует, что типы данных и роли полей (например, «Обязательное») будут сохранены.

Альтернатива: Использование вычисляемых полей

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

Важный момент: если поле участвует в Условном оформлении, оно почти всегда попадает в выборку макета автоматически, поэтому с оформлением проблем обычно меньше, чем с отборами.

Заключение

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

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

← На главную