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