Почему СКД не суммирует ресурсы при выгрузке в таблицу значений и как это исправить?

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

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

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

Выясняем причины некорректного поведения СКД

Основная причина проблемы кроется в оптимизации запросов СКД и особенностях генерации макетов для коллекций значений. Рассмотрим по шагам три ключевых фактора, приводящих к ошибке:

  1. Использование «Детальных записей» вместо группировок. Если в структуре настроек компоновщика оставлены только детальные записи (без явных группировок), СКД по умолчанию считает, что итоги по ресурсам рассчитывать не требуется. В итоговую таблицу просто выводятся детальные строки «как есть». Из-за наличия периодов или иных измерений в промежуточных пакетах запроса база данных возвращает строки, детализированные по дням (где показатель равен 1). Поскольку группировка отсутствует, СКД просто транслирует эти детальные строки в результирующую таблицу значений.
  2. Неверный порядок инициализации компоновщика настроек. Программное добавление ресурсов в схему компоновки часто выполняется с ошибкой последовательности вызовов. Если добавить новое поле итога в СхемаКомпоновкиДанных.ПоляИтога уже после вызова метода Компоновщик.Инициализировать(), то компоновщик настроек просто «не узнает» о существовании нового ресурса. Соответственно, при генерации макета расчет итогов по этому полю производиться не будет.
  3. Неявная оптимизация запроса. СКД анализирует выбранные поля и структуру отчета. Если включено свойство Автозаполнение, система может неявно добавлять поля периодов из временных таблиц в финальный запрос для сохранения логики работы ролей полей. Это приводит к избыточной детализации результирующей выборки.

Ниже подробно описаны четыре практических метода устранения этой проблемы.

Решение 1. Перенос группировки непосредственно в запрос (Способ автора)

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

Рассмотрим, как изменить логику финального пакета. Изначально запрос автора выглядел так:


ВЫБРАТЬ
    НаличиеПоДням.Номенклатура КАК Номенклатура,
    НаличиеПоДням.Характеристика КАК Характеристика,
    СУММА(НаличиеПоДням.ВНаличииДень) КАК ВНаличииДней
ИЗ
    НаличиеПоДням КАК НаличиеПоДням
СГРУППИРОВАТЬ ПО
    НаличиеПоДням.Номенклатура,
    НаличиеПоДням.Характеристика

Если в промежуточной таблице НаличиеПоДням присутствовало поле Период, СКД могла принудительно добавить его в выборку из-за механизма автозаполнения. Чтобы решить эту проблему, перенесем расчет суммы непосредственно во временную таблицу, избавившись от детального поля периода на самом раннем этапе. Изменим предпоследний пакет запроса следующим образом:


ВЫБРАТЬ
    ОстаткиПоДням.Номенклатура КАК Номенклатура,
    ОстаткиПоДням.Характеристика КАК Характеристика,
    СУММА(ВЫБОР
        КОГДА ОстаткиПоДням.ВНаличии > 0
            ТОГДА 1
        ИНАЧЕ 0
    КОНЕЦ) КАК ВНаличииДней
ПОМЕСТИТЬ НаличиеПоДням
ИЗ
    ОстаткиПоДням КАК ОстаткиПоДням
СГРУППИРОВАТЬ ПО
    ОстаткиПоДням.Номенклатура,
    ОстаткиПоДням.Характеристика

В финальном пакете запроса мы просто выбираем уже сгруппированные данные без использования агрегатных функций СУБД. Теперь, даже если СКД выводит отчет через «Детальные записи», СУБД изначально возвращает одну свернутую строку по каждой номенклатуре с готовым числовым значением (например, 30), и единица больше не перезаписывает итоговый результат.

Решение 2. Корректная программная инициализация компоновщика настроек

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

Проанализируем правильный вариант написания кода:


// 1. Сначала вносим изменения непосредственно в СХЕМУ компоновки
ПолеВНаличииДней = СхемаКомпоновкиДанных.ПоляИтога.Добавить(); 
ПолеВНаличииДней.Выражение   = "Сумма(ВНаличииДней)"; 
ПолеВНаличииДней.ПутьКДанным = "ВНаличииДней"; 

// 2. И только после изменения схемы инициализируем компоновщик настроек
Компоновщик = Новый КомпоновщикНастроекКомпоновкиДанных;
Компоновщик.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКомпоновкиДанных));
Компоновщик.ЗагрузитьНастройки(СхемаКомпоновкиДанных.НастройкиПоУмолчанию);

При таком подходе компоновщик примет измененную схему со всеми необходимыми метаданными о ресурсах, и расчет итогов по полю ВНаличииДней отработает корректно при генерации макета.

Решение 3. Программное создание группировок в структуре настроек

При выводе данных в таблицу значений через генератор макета коллекции значений настоятельно рекомендуется отказаться от использования «Детальных записей». Вместо этого следует программно описать структуру группировок. Генератор макета свернет таблицу по указанным разрезам и корректно рассчитает все ресурсы на итоговом уровне.

Посмотрим на пример кода, который заменяет детальные записи на группировку по полям Номенклатура и Характеристика:


// Очищаем стандартную структуру настроек (удаляем Детальные записи)
Компоновщик.Настройки.Структура.Очистить();

// Добавляем группировку первого уровня (Номенклатура)
ГруппировкаНоменклатура = Компоновщик.Настройки.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
ГруппировкаНоменклатура.Использование = Истина;

ПолеГрНоменклатура = ГруппировкаНоменклатура.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
ПолеГрНоменклатура.Использование = Истина;
ПолеГрНоменклатура.Поле = Новый ПолеКомпоновкиДанных("Номенклатура");

// Добавляем подчиненную группировку второго уровня (Характеристика)
ГруппировкаХарактеристика = ГруппировкаНоменклатура.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
ГруппировкаХарактеристика.Использование = Истина;

ПолеГрХарактеристика = ГруппировкаХарактеристика.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
ПолеГрХарактеристика.Использование = Истина;
ПолеГрХарактеристика.Поле = Новый ПолеКомпоновкиДанных("Характеристика");

// Выбираем поле ресурса для вывода на уровне созданной группировки
ВыбранноеПолеРесурса = ГруппировкаХарактеристика.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
ВыбранноеПолеРесурса.Использование = Истина;
ВыбранноеПолеРесурса.Поле = Новый ПолеКомпоновкиДанных("ВНаличииДней");

Благодаря явной группировке компоновщик макета применит агрегатную функцию Сумма к полю ВНаличииДней, и в результирующей таблице значений мы получим ожидаемые суммированные показатели.

Решение 4. Отключение автозаполнения и явная настройка ролей полей

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

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

← На главную