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