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