В процессе разработки отчетов на системе компоновки данных (СКД) программисты часто сталкиваются с классической проблемой: «задвоением» (или дублированием) ресурсов при связи таблиц типа «один-ко-многим». Типичный пример — связь шапки или табличной части родительского документа (например, «Задание на отгрузку») с табличной частью подчиненного документа (например, «Расходная накладная» или «Реализация товаров и услуг»).
Когда к одной строке родительского документа привязывается несколько строк из подчиненных таблиц, данные родителя дублируются в итоговой выборке. В результате стандартное суммирование через ресурс СУММА выдает некорректный, завышенный общий итог. Выясним причину этой проблемы и подробно разберем четыре профессиональных способа её решения.
Рассмотрим ситуацию, описанную пользователем на форуме. У нас есть документ «Задание на отгрузку №1» с общим весом 250 кг. К нему привязаны три расходных документа, которые частично распределяют этот вес:
Обратите внимание: Расходная №2 и Расходная №3 ссылаются на одну и ту же строку «Задания на отгрузку» с весом 150. При обычном левом соединении таблиц в запросе СКД строка с весом 150 продублируется. Если мы просто просуммируем колонку «Вес», то получим: 100 + 150 + 150 = 400. Однако реальный суммарный вес по заданию на отгрузку должен быть равен 250 (100 + 150).
Ниже представлены четыре метода, которые позволят нам рассчитать итоги корректно.
Вместо того чтобы объединять таблицы левым соединением внутри одного SQL-запроса, разработчики платформы 1С рекомендуют разделять данные на два независимых набора данных и связывать их на уровне самой СКД. Это наиболее надежный и «родной» механизм системы компоновки.
Рассмотрим этот процесс по шагам:
Почему это решает проблему задвоения? При использовании связей наборов данных СКД рассчитывает итоги по каждому набору абсолютно независимо. Вес задания на отгрузку будет просуммирован строго по первому набору (где нет дублей строк), а детальные данные по расходным накладным корректно «пристыкуются» к отчету без искажения итоговых сумм.
Если по каким-то причинам нам необходимо оставить левое соединение внутри одного запроса, мы можем использовать мощный аналитический инструмент СКД — функцию ВычислитьВыражениеСГруппировкойМассив. Данный механизм обходит ограничение платформы на использование вложенных агрегатных функций (напрямую написать Сумма(Максимум(Вес)) в СКД нельзя — система выдаст ошибку компоновки).
Для реализации этого метода выполним следующие настройки:
Вес в колонке «Выражение» пропишем формулу:
Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(Вес)", "ЗаданиеНаПеревозку"))
Проанализируем, как работает эта формула:
ЗаданиеНаПеревозку (второй параметр).Максимум(Вес). Поскольку в рамках одного документа вес строки всегда одинаковый, функции Максимум, Минимум или Среднее вернут одно и то же число для конкретного документа (в нашем примере — 150 вместо двух строк по 150).Сумма складывает полученные уникальные значения элементов массива на уровне общих итогов, что дает корректный результат (250).Важная тонкость: Если в вашем отчете присутствуют другие вышестоящие группировки (например, Организация -> Склад -> ЗаданиеНаПеревозку), во втором параметре функции (выражение полей группировки) необходимо перечислить их через запятую в кавычках. Это позволит СКД правильно собирать массив уникальных значений на каждом уровне иерархии:
Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(Вес)", "Организация, Склад, ЗаданиеНаПеревозку"))
Проблему дублирования строк можно решить еще на уровне базы данных (в СУБД) с помощью механизма временных таблиц. Этот способ отлично подходит, когда мы хотим получить чистый и быстрый запрос без сложных формул в ресурсах СКД.
Посмотрим на пример построения такого запроса:
ВЫБРАТЬ
Реализация.Ссылка КАК Реализация,
Реализация.Сделка КАК ЗаданиеНаОтгрузку,
СУММА(Реализация.Вес) КАК ВесВРеализации
ПОМЕСТИТЬ ВТ_Реализации
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
СГРУППИРОВАТЬ ПО
Реализация.Ссылка,
Реализация.Сделка;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Задание.Ссылка КАК ЗаданиеНаОтгрузку,
Задание.Вес КАК ВесЗадания,
ЕСТЬNULL(ВТ_Реализации.ВесВРеализации, 0) КАК ВесВРеализации
ИЗ
Документ.ЗаданиеНаОтгрузку КАК Задание
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Реализации КАК ВТ_Реализации
ПО ВТ_Реализации.ЗаданиеНаОтгрузку = Задание.Ссылка
В данном случае мы предварительно сворачиваем подчиненные данные по документу-основанию во временной таблице ВТ_Реализации. В основном запросе реальная таблица соединяется со сгруппированной временной таблицей. Связь получается «один-к-одному», строки не размножаются, а итоги по весу считаются стандартной суммой без искажений.
Минус метода: он применим только в том случае, если в финальном отчете не требуется выводить детальные строки по каждой отдельной расходной накладной (так как во временной таблице мы их уже свернули).
В СКД на вкладке «Ресурсы» мы можем гибко распределять формулы для разных уровней отчета, используя колонку «Рассчитывать по...».
Настроим расчет ресурса следующим образом:
Вес в таблицу ресурсов первый раз. В выражении укажем Максимум(Вес), а в колонке «Рассчитывать по...» выберем группировку ЗаданиеНаПеревозку. Это гарантирует, что на уровне самого документа вес не задвоится.Вес в таблицу ресурсов второй раз. В поле «Выражение» пропишем формулу с ВычислитьВыражениеСГруппировкойМассив, а в колонке «Рассчитывать по...» укажем «Общий итог» и другие вышестоящие группировки.0, чтобы не путать пользователя повторяющимися цифрами.Таким образом, мы детально разобрали причины возникновения проблемы задвоения ресурсов в СКД и изучили четыре надежных способа её устранения. Выбор конкретного метода зависит от архитектуры вашего отчета и требований к детализации данных.