Как в СКД посчитать итог вышестоящей группировки без учета подчиненной и избежать задвоения

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

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

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

Причина возникновения проблемы задвоения

Прежде чем перейти к решениям, проанализируем, почему так происходит. Когда мы соединяем в одном запросе таблицы с разной степенью детализации (например, остатки товаров и цены), СУБД возвращает декартово произведение строк. Если для одной характеристики товара существует три типа цен, то строка с количеством прихода этого товара продублируется трижды — по одной строке на каждый тип цен.

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

---

Решение 1. Раздельный расчет ресурсов для разных группировок (через ВычислитьВыражениеСГруппировкойМассив)

Самый быстрый способ исправить итоги — настроить расчет одного и того же ресурса по-разному для каждого уровня группировки. В СКД мы можем добавлять одно и то же поле в список ресурсов несколько раз, указывая в колонке "Рассчитывать по..." конкретные группировки.

Рассмотрим по шагам, как настроить такую схему:

  1. Откроем вкладку "Ресурсы" в схеме компоновки данных.
  2. Добавим поле КоличествоЕдПриход для подчиненных уровней (где данные не должны суммироваться по типам цен).
  3. Добавим это же поле второй раз для расчета итогов по вышестоящим уровням с использованием специальной функции.

Пропишем выражения для каждого уровня:

Шаг A. Настройка для детальных записей и подчиненных группировок

Для группировок Характеристика и ТипЦен нам нужно брать не сумму, а максимальное или среднее значение, поскольку количество прихода для конкретной характеристики неизменно в разрезе цен. В поле "Выражение" пишем:

Максимум(КоличествоЕдПриход)

В колонке "Рассчитывать по..." указываем только: Характеристика, ТипЦен.

Шаг B. Настройка для вышестоящих группировок

Для группировок Номенклатура, Склад и для Общего итога нам нужно сложить максимальные значения, полученные на уровне характеристик. Для этого мы используем функцию ВычислитьВыражениеСГруппировкойМассив. В поле "Выражение" пишем:


Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(КоличествоЕдПриход)", "Номенклатура, Характеристика"))

В колонке "Рассчитывать по..." указываем: Номенклатура, Склад, ОбщиеИтоги (или оставляем это поле пустым, тогда формула применится ко всем остальным группировкам по умолчанию).

Важный нюанс: Обратите внимание на второй параметр функции ВычислитьВыражениеСГруппировкойМассив. Рекомендуется указывать связку полей через запятую — "Номенклатура, Характеристика". Если указать только "Характеристика", СКД выполнит группировку по характеристикам глобально во всем отчете. Если у разной номенклатуры совпадут наименования или коды характеристик, это приведет к некорректному расчету общих итогов.

---

Решение 2. Разделение запроса на несколько наборов данных (Рекомендуемый подход)

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

Выясним, как это реализовать:

  1. Создаем Набор данных 1 (Запрос): Этот набор будет получать только количественные показатели. В его запросе не должно быть никаких упоминаний о типах цен и других подчиненных сущностях. Выбираем поля: Склад, Номенклатура, Характеристика, КоличествоЕдПриход.
  2. Создаем Набор данных 2 (Запрос): Этот набор будет получать информацию о типах цен и ценах в разрезе номенклатуры и характеристик.
  3. Настраиваем связи наборов данных: Переходим на вкладку "Связи наборов данных". Создаем связь от Набора 1 (источник) к Набору 2 (приемник) по полям Номенклатура и Характеристика.

Почему этот подход решает проблему задвоения:

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

---

Решение 3. Использование отборов внутри функции ВычислитьВыражениеСГруппировкойМассив

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


ВычислитьВыражениеСГруппировкойМассив(<Выражение>, <ВыраженияПолейГруппировки>, <ОтборЗаписей>, <ОтборГруппировок>)

Проанализируем параметры фильтрации:

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

---

Резюме

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

← На главную