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