Почему СКД изменяет цифры из запроса и как получить корректные данные

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

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

Анализ причин расхождения данных между консолью запросов и СКД

Основное отличие выполнения запроса в СКД от обычной консоли заключается в работе механизма Автозаполнения и оптимизации текста запроса платформой 1С. Выделим две ключевые причины возникновения расхождений:

  1. Размножение строк при соединении «один-ко-многим» и некорректная агрегация: если показатель рассчитан на уровне родительской сущности (например, Производитель), но в финальной выборке соединяется с подчиненной таблицей (например, Номенклатура), значение производителя дублируется в каждой строке номенклатуры. Если СКД суммирует это поле по умолчанию, итоговая сумма умножается на количество подчиненных позиций.
  2. Модификация текста запроса оптимизатором СКД: механизм компоновки данных анализирует выбранные пользователем поля и группировки. Если поле или измерение из подзапроса не выведено в финальный отчет или не участвует в группировках, СКД может автоматически исключить его из секций ВЫБРАТЬ и СГРУППИРОВАТЬ ПО внутренних подзапросов. В результате подзапросы сворачиваются иначе, чем задумано разработчиком.

Способ 1. Точная настройка расчета ресурсов по группировкам

Если числовое поле рассчитано на верхнем уровне группировки (например, для производителя), его нельзя просто складывать стандартной функцией Сумма(...) на всех уровнях отчета. Настроим вычисление ресурса только для нужного уровня структуры:

  1. Перейдем на вкладку Ресурсы в схеме компоновки данных.
  2. В колонке Рассчитывать по... выберем конкретную группировку — Производитель.
  3. Для детальных записей и вложенных группировок (например, Номенклатура) оставим поле незаполненным либо укажем агрегатную функцию Максимум(РасчетПоПроизводителю).

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


Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(РасчетПоПроизводителю)", "Производитель"))

Такое выражение вычисляет максимальное (уникальное) значение для каждого производителя и затем суммирует полученный массив без повторений из-за подчиненных строк номенклатуры.

Способ 2. Вывод показателя как реквизита группировки

Если значение является справочным или расчетным показателем верхнего уровня и его вовсе не требуется агрегировать в общие итоги отчета, исключим его из вкладки Ресурсы:

  1. Удалим поле из списка ресурсов схемы.
  2. В настройках варианта отчета на вкладке Настройки выберем группировку Производитель.
  3. В окне Выбранные поля для группировки производителя добавим наше поле расчета. В этом случае значение выведется в строке производителя один раз без искажений.

Способ 3. Фиксация полей и таблиц в запросе СКД

Чтобы запретить оптимизатору СКД исключать важные поля и группировки из подзапросов при включенном автозаполнении, применим синтаксис расширения языка СКД с помощью фигурных скобок:

  1. В тексте запроса внутри подзапросов или временных таблиц явно укажем обязательные поля в секциях расширения {ВЫБРАТЬ ...}.
  2. На вкладке Компоновка данных в конструкторе запроса отметим критически важные таблицы и поля как обязательные.
  3. Во временных таблицах используем конструкцию ВЫБРАТЬ РАЗЛИЧНЫЕ или явное группирование по ключевым измерениям, чтобы исключить дублирование строк до момента соединения.

Способ 4. Просмотр реального скомпонованного запроса

Чтобы точно увидеть, как именно СКД перестроила исходный текст запроса, сформируем макет компоновки программно в модуле отчета или внешней обработке:


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

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

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

Способ 5. Использование внешнего набора данных (Таблица значений)

Если логика пакетного запроса слишком сложна и оптимизатор СКД продолжает искажать промежуточные расчеты, вынесем выполнение запроса в программный код:

  1. В схеме компоновки данных заменим набор данных типа Запрос на набор данных типа Объект с именем, например, ТаблицаДанных.
  2. Опишем структуру полей набора данных вручную в СКД.
  3. В модуле объекта отчета в процедуре ПриКомпоновкеРезультата выполним запрос стандартным объектом платформы Запрос и передадим результат в процессор компоновки данных:

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

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

← На главную