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