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