При разработке отчетов в системе компоновки данных (СКД) часто возникает задача анализа динамики производства. Одной из специфических задач является подсчет количества периодов (колонок в кросс-таблице), начиная с того момента, когда была зафиксирована первая выработка продукции. Сложность заключается в том, что если в последующих периодах выработка отсутствует (равна нулю), стандартные механизмы нарастающего итога в СКД могут «терять» значения или возвращать NULL, прекращая корректный инкремент счетчика.
В этой статье мы разберем, почему стандартное выражение ВычислитьВыражение может выдавать неверный результат, и рассмотрим несколько надежных способов реализации этой логики.
Рассмотрим ситуацию, описанную автором: в кросс-таблице по строкам идет продукция, по колонкам — периоды. Нужно создать поле, которое будет показывать «возраст» производства: в первом периоде с производством — 1, во втором — 2 и так далее, даже если во втором периоде количество равно 0. Основная причина проблемы кроется в том, как СКД обрабатывает отсутствие записей в базе данных для конкретного пересечения строки и колонки.
Если в периоде 10:00–11:00 не было движений, то в результате запроса записи для этого часа может просто не существовать. В этом случае функция ВычислитьВыражение, обращаясь к группировке "Период", может возвращать NULL или предыдущее значение, так как контекст вычисления для «пустой» ячейки не определен. Нам же необходимо принудительно заставить систему считать каждый период после «точки старта».
Один из самых элегантных способов — это сначала определить дату (или номер периода) самой первой выработки для каждой продукции, а затем в каждой колонке сравнивать текущий период с этой датой. Проанализируем этот алгоритм по шагам.
ДатаПервойВыработки. Однако, лучше вычислять его сразу как ресурс, чтобы иметь доступ к агрегатным функциям.
ВычислитьВыражение("Минимум(Выбор Когда Сумма(Количество) > 0 Тогда Период Конец)", , , "Первая", "Последняя")
ПорядковыйНомерПериода), который будет считать количество колонок:
ВычислитьВыражение("Количество(Различные Период)", "Период", , "Первая", "Текущая")
Если логика в СКД становится слишком громоздкой, мы можем перенести часть вычислений в запрос. Это обеспечит более стабильную работу при расшифровках отчета. Проанализируем ситуацию: нам нужно для каждой записи знать, была ли уже когда-то в прошлом выработка по этой номенклатуре.
Разберем пример создания временной таблицы в запросе:
ВЫБРАТЬ
Номенклатура,
МИНИМУМ(Период) КАК ДатаСтарта
ПОМЕСТИТЬ ВТ_ПерваяВыработка
ИЗ
РегистрНакопления.ВыпускПродукции
ГДЕ
Количество > 0
СГРУППИРОВАТЬ ПО
Номенклатура
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ОсновнаяТаблица.Номенклатура,
ОсновнаяТаблица.Период,
ОсновнаяТаблица.Количество,
ВЫБОР
КОГДА ОсновнаяТаблица.Период >= ЕСТЬNULL(ВТ_ПерваяВыработка.ДатаСтарта, ДАТАВРЕМЯ(3999, 12, 31))
ТОГДА 1
ИНАЧЕ 0
КОНЕЦ КАК ФлагПроизводствоНачато
ИЗ
ОсновнаяТаблица КАК ОсновнаяТаблица
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ПерваяВыработка КАК ВТ_ПерваяВыработка
ПО ОсновнаяТаблица.Номенклатура = ВТ_ПерваяВыработка.Номенклатура
После такой подготовки в СКД нам остается только просуммировать поле ФлагПроизводствоНачато нарастающим итогом по группировке "Период". Выражение ресурса будет выглядеть так:
ВычислитьВыражение("Сумма(ФлагПроизводствоНачато)", "Период", , "Первая", "Текущая")
Выясним причину, по которой обычная сумма может не работать в кросс-таблице. Если в периоде нет данных, СКД не «видит» строку. Чтобы это обойти, можно использовать массив. Рассмотрим подробнее этот метод. Мы собираем состояния всех предыдущих периодов в массив и считаем его размер или сумму его элементов.
В ресурсах для поля Поле1 можно прописать следующую конструкцию:
Сумма(ВычислитьВыражениеСГруппировкойМассив("Выбор Когда ВычислитьВыражение(""Сумма(Количество)"", ""Период"", , ""Первая"", ""Текущая"") > 0 Тогда 1 Иначе 0 Конец", "Период"))
Важный нюанс: СКД часто ругается на вложенные агрегатные функции (сообщение №10 на форуме). Чтобы избежать ошибки «Вложенные агрегатные функции не допускаются», необходимо убедиться, что внешняя функция Сумма применяется к массиву, который уже содержит вычисленные значения по каждой группировке.
Чтобы колонки с нулевыми значениями вообще отображались в отчете и в них производились вычисления, необходимо правильно настроить поле Период в схеме компоновки данных. Выполним следующие действия:
Период и в колонке "Роль" нажмем на кнопку редактирования.Период и установим свойство "Тип дополнения" (например, "День").Почему это важно: когда включено дополнение периодов, СКД генерирует виртуальные записи для «пустых» дат. В этом случае функция ВычислитьВыражение будет иметь физическую ячейку для выполнения расчета, и ваш счетчик 1, 2, 3, 4 не прервется, даже если производство в эти дни простаивало.
Проанализируем ситуацию из сообщения №14, где компоновщик заменяет выражение на NULL. Это часто происходит, если поле, по которому идет упорядочивание в функции ВычислитьВыражение, отсутствует в текущем контексте группировки.
Всегда проверяйте четвертый параметр функции — Сортировка. Если вы считаете итоги по периоду, который является датой, СКД должна четко понимать порядок следования:
ВычислитьВыражение("Сумма(Поле1)", "Период", "Очередность", "Первая", "Текущая")
Где "Очередность" — это направление сортировки. Также рекомендуется оборачивать суммируемые поля в ЕстьNull(Сумма(Количество), 0) прямо внутри строки выражения, чтобы математические операции не приводили к неопределенному результату.
Подводя итог, можно сказать, что наиболее стабильным решением является сочетание дополнения периодов в настройках СКД и использования нарастающего итога по флагу, предварительно вычисленному в запросе или через минимальную дату старта в ресурсах. Это гарантирует, что каждый столбец отчета будет содержать корректный порядковый номер, независимо от наличия физических записей в информационной базе.