Как избежать задвоения позиций и сумм в запросе для СКД?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Часто при разработке отчетов на базе Системы Компоновки Данных (СКД) мы сталкиваемся с проблемой задвоения данных. Разберем типичную ситуацию: мы собираем суммы по документам, и нам нужно вывести в отчет или добавить в отборы информацию о сегментах партнеров. Поскольку один контрагент может входить сразу в несколько сегментов, в результате выполнения запроса строки с документами дублируются. Как следствие, задваиваются и суммы в общих итогах отчета.

Давайте проанализируем исходный код, который приводит к такой ошибке:


ВЫБРАТЬ
    ВТ.Ссылка КАК Ссылка,
    ВТ.Сумма КАК Сумма,
    ВТ.Партнер КАК Партнер,
    ПартнерыСегмента.Сегмент КАК Сегмент
ИЗ
    ВТ КАК ВТ
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПартнерыСегмента КАК ПартнерыСегмента
        ПО ВТ.Партнер = ПартнерыСегмента.Партнер

При таком левом соединении на каждую найденную запись в регистре сведений формируется новая строка. Если контрагент состоит в двух сегментах, один документ отразится двумя строками с одной и той же суммой. Рассмотрим подробнее несколько эффективных способов решения этой проблемы.

Способ 1. Использование необязательных связей в СКД

Самый быстрый и изящный способ, если поле Сегмент нужно нам исключительно для отборов, а выводить его в детальных записях не планируется. Нам нужно сделать так, чтобы СКД выполняла присоединение таблицы регистра только тогда, когда пользователь накладывает фильтр.

  1. Откроем конструктор запроса в схеме компоновки данных.
  2. Перейдем на вкладку Компоновка данных.
  3. В разделе таблиц найдем нашу присоединенную таблицу РегистрСведений.ПартнерыСегмента.
  4. Установим для нее флажок Необязательная (если стояла галка "Обязательная" — снимем ее).
  5. Уберем поле Сегмент из выбранных полей и оставим его только в доступных полях для отбора.

На уровне кода СКД это реализуется через специальный синтаксис расширения языка запросов с использованием фигурных скобок:


{ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПартнерыСегмента КАК ПартнерыСегмента 
ПО ВТ.Партнер = ПартнерыСегмента.Партнер}

Теперь платформа динамически сформирует итоговый запрос перед отправкой на сервер СУБД. Если пользователь не использует отбор по сегменту, платформа просто вырежет этот JOIN из запроса, гарантируя отсутствие задвоений.

Способ 2. Использование нескольких наборов данных

Это фундаментальный и самый правильный архитектурный механизм СКД, который отлично работает, если поле необходимо выводить в самом отчете. Создадим два независимых набора данных:

  1. Набор 1 (Документы): Выбираем только документы, суммы и партнеров.
  2. Набор 2 (Сегменты): Отдельным запросом выбираем только партнеров и их сегменты.
  3. Переходим на вкладку Связи наборов данных и указываем связь между наборами по полю Партнер.

При таком подходе движок компоновки берет математику вычисления итогов на себя. Суммы из первого набора будут рассчитаны корректно в общих итогах. Даже если у партнера 5 сегментов, суммы документов умножаться на 5 не будут.

Способ 3. Вывод всех сегментов в одну строку

Если по бизнес-логике нам обязательно нужно показать в отчете, к каким сегментам относится партнер, но нужно избежать появления новых строк (вывести документ одной строкой), применим возможности языка выражений СКД.

Настроим свертку значений в строку. Для этого перейдем на вкладку Ресурсы (или Вычисляемые поля) и для поля Сегмент напишем следующее выражение:

СоединитьСтроки(Массив(Сегмент), ", ")

Задвоения строк не произойдет. В колонке отчета мы получим аккуратный список через запятую, например: "Оптовики, VIP-клиенты".

Способ 4. Правильная настройка итогов по группировкам

Посмотрим на ситуацию, когда структура отчета требует вывода детальных записей, дублирование строк допустимо для детализации, но нам критически важно сохранить правильные итоги.

Перейдем на вкладку Ресурсы. В колонке Рассчитывать по... укажем конкретные группировки. Таким образом мы явно сообщаем СКД, на каких уровнях группировки необходимо суммировать ресурс (например, только по уровню Документ). Для вышестоящих уровней можно использовать агрегатную функцию МАКСИМУМ вместо СУММА, чтобы предотвратить сложение сдублированных чисел в общих итогах.

Способ 5. Выделение «основного» значения в запросе

Проанализируем еще один подход. Если нас устроит вывод только одного сегмента (любого из списка), мы можем отсечь дубли еще до передачи данных в механизм компоновки — в самом теле запроса.

Сгруппируем таблицу регистра сведений перед присоединением, используя функцию МАКСИМУМ(Сегмент), либо присоединим таблицу как вложенный запрос:


ВЫБРАТЬ ПЕРВЫЕ 1 
    Сегмент 
ИЗ 
    РегистрСведений.ПартнерыСегмента 
ГДЕ 
    Партнер = ВТ.Партнер

Каждый из этих методов решает задачу по-своему. Выбор конкретного варианта зависит от того, нужно ли вам просто фильтровать данные, показывать несколько значений в одной ячейке или строить сложную многоуровневую структуру.

← На главную