При разработке отчетов на базе системы компоновки данных (СКД) разработчики периодически сталкиваются с ситуацией, когда числовые показатели выводятся некорректно. Например, в информационной базе зарегистрировано 10 документов с одинаковой суммой 8000 рублей, однако при формировании отчета с группировками верхнего уровня итоговая сумма выводится как 8000 вместо положенных 80 000 рублей. Если же добавить в структуру группировку по детальным записям или регистратору, либо изменить суммы документов так, чтобы они различались, отчет начинает рассчитывать результат верно.
Разберем детально механизм возникновения этой ошибки и рассмотрим практические способы ее устранения.
Проанализируем типовую ситуацию на примере запроса набора данных, формирующего остатки и движения денежных средств:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ДвиженияДенежныхСредствОбороты.ВидДенежныхСредств КАК ВидДенежныхСредств,
ДвиженияДенежныхСредствОбороты.СтатьяДвиженияДенежныхСредств КАК СтатьяДвиженияДенежныхСредств,
ДвиженияДенежныхСредствОбороты.ДокументДвижения,
ВЫБОР
КОГДА ДвиженияДенежныхСредствОбороты.ПриходРасход.Порядок = 0
ТОГДА ДвиженияДенежныхСредствОбороты.СуммаОборот
ИНАЧЕ 0
КОНЕЦ КАК Приход,
ВЫБОР
КОГДА ДвиженияДенежныхСредствОбороты.ПриходРасход.Порядок = 1
ТОГДА ДвиженияДенежныхСредствОбороты.СуммаОборот
ИНАЧЕ 0
КОНЕЦ КАК Расход,
ДвиженияДенежныхСредствОбороты.Организация КАК Организация,
ВЫБОР
КОГДА ДвиженияДенежныхСредствОбороты.Подразделение <> ЗНАЧЕНИЕ(Справочник.Подразделения.ПустаяСсылка)
ТОГДА ДвиженияДенежныхСредствОбороты.Подразделение
ИНАЧЕ ДвиженияДенежныхСредствОбороты.ДокументДвижения.Подразделение
КОНЕЦ КАК Подразделение
ПОМЕСТИТЬ Обороты
ИЗ
РегистрНакопления.ДвиженияДенежныхСредств.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ДвиженияДенежныхСредствОбороты
СГРУППИРОВАТЬ ПО
ДвиженияДенежныхСредствОбороты.ВидДенежныхСредств,
ДвиженияДенежныхСредствОбороты.СтатьяДвиженияДенежныхСредств,
ДвиженияДенежныхСредствОбороты.ДокументДвижения,
ВЫБОР
КОГДА ДвиженияДенежныхСредствОбороты.ПриходРасход.Порядок = 0
ТОГДА ДвиженияДенежныхСредствОбороты.СуммаОборот
ИНАЧЕ 0
КОНЕЦ,
ВЫБОР
КОГДА ДвиженияДенежныхСредствОбороты.ПриходРасход.Порядок = 1
ТОГДА ДвиженияДенежныхСредствОбороты.СуммаОборот
ИНАЧЕ 0
КОНЕЦ,
ДвиженияДенежныхСредствОбороты.Организация,
ВЫБОР
КОГДА ДвиженияДенежныхСредствОбороты.Подразделение <> ЗНАЧЕНИЕ(Справочник.Подразделения.ПустаяСсылка)
ТОГДА ДвиженияДенежныхСредствОбороты.Подразделение
ИНАЧЕ ДвиженияДенежныхСредствОбороты.ДокументДвижения.Подразделение
КОНЕЦ
Выясним причину такого поведения платформы. Ошибка возникает из-за сочетания двух факторов:
Приход и Расход не рассчитываются через агрегатную функцию СУММА(), а включены напрямую в блок СГРУППИРОВАТЬ ПО. Пока в выборке присутствует уникальный регистратор ДокументДвижения, строки не склеиваются, так как каждая запись уникальна.ДокументДвижения не выводится в выбранных полях отчета и не участвует в группировках, компоновщик макета СКД в целях оптимизации исключает неиспользуемое поле из секций ВЫБРАТЬ и СГРУППИРОВАТЬ ПО. Лишившись уникального ключа, СУБД группирует строки только по оставшимся измерениям и полям сумм. В результате все 10 записей с суммой 8000 признаются абсолютно одинаковыми и схлопываются в одну строку.Рассмотрим правильный архитектурный подход к устранению проблемы. Необходимо переписать запрос так, чтобы суммовые поля агрегировались стандартными средствами языка запросов, а не группировались:
ВЫБРАТЬ в агрегатную функцию СУММА(...).СГРУППИРОВАТЬ ПО.Приход и Расход, задав для них выражение расчета итогов: Сумма(Приход) и Сумма(Расход).Посмотрим на пример исправленного фрагмента запроса:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ДвиженияДенежныхСредствОбороты.ВидДенежныхСредств КАК ВидДенежныхСредств,
ДвиженияДенежныхСредствОбороты.СтатьяДвиженияДенежныхСредств КАК СтатьяДвиженияДенежныхСредств,
ДвиженияДенежныхСредствОбороты.ДокументДвижения КАК ДокументДвижения,
СУММА(ВЫБОР
КОГДА ДвиженияДенежныхСредствОбороты.ПриходРасход.Порядок = 0
ТОГДА ДвиженияДенежныхСредствОбороты.СуммаОборот
ИНАЧЕ 0
КОНЕЦ) КАК Приход,
СУММА(ВЫБОР
КОГДА ДвиженияДенежныхСредствОбороты.ПриходРасход.Порядок = 1
ТОГДА ДвиженияДенежныхСредствОбороты.СуммаОборот
ИНАЧЕ 0
КОНЕЦ) КАК Расход,
ДвиженияДенежныхСредствОбороты.Организация КАК Организация,
ВЫБОР
КОГДА ДвиженияДенежныхСредствОбороты.Подразделение <> ЗНАЧЕНИЕ(Справочник.Подразделения.ПустаяСсылка)
ТОГДА ДвиженияДенежныхСредствОбороты.Подразделение
ИНАЧЕ ДвиженияДенежныхСредствОбороты.ДокументДвижения.Подразделение
КОНЕЦ КАК Подразделение
ПОМЕСТИТЬ Обороты
ИЗ
РегистрНакопления.ДвиженияДенежныхСредств.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ДвиженияДенежныхСредствОбороты
СГРУППИРОВАТЬ ПО
ДвиженияДенежныхСредствОбороты.ВидДенежныхСредств,
ДвиженияДенежныхСредствОбороты.СтатьяДвиженияДенежныхСредств,
ДвиженияДенежныхСредствОбороты.ДокументДвижения,
ДвиженияДенежныхСредствОбороты.Организация,
ВЫБОР
КОГДА ДвиженияДенежныхСредствОбороты.Подразделение <> ЗНАЧЕНИЕ(Справочник.Подразделения.ПустаяСсылка)
ТОГДА ДвиженияДенежныхСредствОбороты.Подразделение
ИНАЧЕ ДвиженияДенежныхСредствОбороты.ДокументДвижения.Подразделение
КОНЕЦ
В этом случае, даже если компоновщик исключит ДокументДвижения из результирующего SQL-запроса, СУБД корректно сложит 10 записей и вернет 80 000 рублей.
Разберем быстрый способ исправления в настройках СКД без глубокой модификации логики запроса:
ДокументДвижения.Особенности механизма: Установка свойства «Обязательное» запрещает оптимизатору СКД вырезать данное поле из секций ВЫБРАТЬ и СГРУППИРОВАТЬ ПО, даже если поле не выводится пользователю на экран. Поле всегда будет присутствовать в запросе на уровне СУБД, что сохранит детальность строк и предотвратит их объединение. Однако следует помнить, что это решение создает дополнительную нагрузку на сервер баз данных при обработке больших массивов информации.
Рассмотрим альтернативный вариант управления запросом. Если снять флаг Автозаполнение на вкладке набора данных, СКД полностью прекратит динамическую модификацию секций ВЫБРАТЬ и СГРУППИРОВАТЬ ПО.
При использовании данного способа все поля отбора, группировок и порядка потребуется настраивать вручную с использованием специального синтаксиса расширения языка запросов СКД (конструкции вида {ВЫБРАТЬ ...}, {ГДЕ ...}).