Разберем распространенную задачу при разработке отчетов на базе системы компоновки данных (СКД). Часто требуется свести в одну таблицу остатки товаров в разрезе партий и зарезервированное количество, при этом в регистре резервов аналитический разрез по партиям отсутствует.
Если связать два таких регистра через левое соединение в едином запросе, возникает проблема разной гранулярности данных: одна запись с резервом привязывается к нескольким строкам с партиями. В результате стандартное суммирование приводит к задвоению резервов, а значение резерва отображается в каждой детальной строке партии, что искажает итоговый отчет.
Проанализируем исходный запрос и выясним причины такого поведения платформы, а затем рассмотрим несколько проверенных способов решения этой проблемы.
Посмотрим на типовой запрос, который приводит к размножению строк:
ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
ОстаткиТоваров.Склад КАК Склад,
ОстаткиТоваров.Товар КАК Товар,
ОстаткиТоваров.КоличествоОстаток КАК Остаток,
ЕСТЬNULL(РезервТоваров.КоличествоОстаток, 0) КАК Резерв,
ОстаткиТоваров.Партия КАК Партия
ИЗ
РегистрНакопления.ОстаткиТоваров.Остатки(&Период, ) КАК ОстаткиТоваров
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.РезервТоваров.Остатки(&Период, ) КАК РезервТоваров
ПО (РезервТоваров.Склад = ОстаткиТоваров.Склад)
И (РезервТоваров.Товар = ОстаткиТоваров.Товар)
В результате такого соединения для одного товара с тремя партиями формируются три записи в выборке. Значение из поля Резерв дублируется во всех трех строках. Если для ресурса указана функция Сумма(Резерв), СКД суммирует резерв по каждой строке партии, что дает неверный результат. Если указать Максимум(Резерв), общий итог сойдется, но цифра резерва продолжит выводиться напротив каждой отдельной партии.
Рассмотрим несколько рабочих подходов для корректного расчета и отображения показателей.
Этот способ является наиболее простым и правильным с точки зрения архитектуры СКД. Он позволяет вывести колонку с резервом на уровне товара и полностью убрать ее со строк с партиями.
Разберем по шагам порядок настройки:
Товар, внутри нее — подчиненная группировка Партия.Товар) и на вкладке Выбранные поля укажем: Остаток и Резерв.Партия, перейдем на ее вкладку Выбранные поля и отключим автонаследование полей (снимем флаг с элемента Автополе или удалим его).Партия только поле Остаток.В результате колонка Резерв будет заполняться только на уровне строки Товар, а на детальных строках партий останутся только их фактические остатки.
Если необходимо, чтобы в строках партий отображалось нулевое значение или выполнялся специфический расчет, настроим выражения на вкладке Ресурсы.
Посмотрим на пример формулы с условием проверки заполненности партии:
Выбор
Когда ЗначениеЗаполнено(Партия) Тогда 0
Иначе Сумма(Резерв)
Конец
Для корректной работы СКД выполним следующие действия:
Резерв укажем приведенное выше выражение.Товар, Склад и ОбщийИтог.Обратите внимание: если добавить одно и то же поле в список ресурсов несколько раз без точного указания списка группировок в колонке Рассчитывать по..., платформа выдаст ошибку Повторяющееся поле итога. Каждая строка ресурса должна иметь строго определенный и непересекающийся список группировок расчета.
Чтобы избежать денормализации данных и дублирования строк еще на этапе выполнения запроса к СУБД, заменим ЛЕВОЕ СОЕДИНЕНИЕ на объединение выборок.
Рассмотрим структуру такого запроса:
ВЫБРАТЬ
ОстаткиТоваров.Склад КАК Склад,
ОстаткиТоваров.Товар КАК Товар,
ОстаткиТоваров.Партия КАК Партия,
ОстаткиТоваров.КоличествоОстаток КАК Остаток,
0 КАК Резерв
ИЗ
РегистрНакопления.ОстаткиТоваров.Остатки(&Период, ) КАК ОстаткиТоваров
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
РезервТоваров.Склад,
РезервТоваров.Товар,
ЗНАЧЕНИЕ(Справочник.Партии.ПустаяСсылка),
0,
РезервТоваров.КоличествоОстаток
ИЗ
РегистрНакопления.РезервТоваров.Остатки(&Период, ) КАК РезервТоваров
Преимущества такого подхода:
Остаток и Резерв) можно использовать стандартное агрегатное выражение Сумма(...).Товар резервы и остатки суммируются корректно, а внутри строк с заполненными партиями колонка резерва естественным образом содержит ноль.Если логика отчета требует разделения данных, создадим два независимых набора данных (НаборДанных1 — по остаткам, НаборДанных2 — по резервам).
Разберем порядок связывания наборов:
Склад = Склад и Товар = Товар.Резерв подключаем в выбранные поля группировки Товар. Для группировки Партия это поле не выбирается.СКД автоматически выполнит выборку резервов только при расчете итогов по группировке Товар, исключая дублирование данных в детальных строках партий.