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