При разработке отчетов в Системе компоновки данных (СКД) программисты часто сталкиваются с проблемой «размножения» данных. Типичный пример: необходимо вывести остатки товаров и их цены. Если у одного товара есть несколько типов цен (например, оптовая и розничная), то при обычном левом соединении в запросе одна строка с остатком превращается в две или более. В результате стандартный ресурс Сумма(Количество) выдает некорректный результат, складывая один и тот же остаток столько раз, сколько цен найдено для товара.
В этой статье мы разберем, почему это происходит и как правильно настроить расчет ресурсов, чтобы итоги оставались верными вне зависимости от детализации цен.
Разберем ситуацию на примере. Допустим, на складе числится Товар1 в количестве 10 штук. У этого товара есть две цены: 500 рублей и 700 рублей. После выполнения запроса с соединением таблиц остатков и цен система получит следующие строки:
Когда СКД рассчитывает ресурс Сумма(Количество), она видит две записи и честно складывает их, получая 20 штук. Это происходит из-за формирования декартового произведения на уровне SQL-запроса. Для решения этой проблемы существует несколько подходов.
Этот метод позволяет агрегировать данные внутри группировки по определенным правилам, игнорируя дублирующиеся строки. Рассмотрим, как правильно настроить выражение ресурса. Вместо простого суммирования в поле «Выражение» на вкладке «Ресурсы» необходимо прописать специальную конструкцию.
Проанализируем логику: нам нужно, чтобы для каждой конкретной номенклатуры брался только один показатель остатка (например, максимальный), а уже эти полученные значения суммировались по иерархии папок или общему итогу.
Итоговое выражение будет выглядеть следующим образом:
Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(Остаток)", "Номенклатура"))
Разберем по шагам, как работает эта формула:
ВычислитьВыражениеСГруппировкойМассив — функция создает массив значений, предварительно сгруппировав их по указанному полю."Максимум(Остаток)" — это первый параметр. Он указывает, что внутри каждой группы (в нашем случае внутри каждой номенклатуры) нужно взять максимальное значение остатка. Поскольку остаток в дублях одинаковый, максимум из 10 и 10 даст нам 10."Номенклатура" — второй параметр. Это измерение, по которому происходит сворачивание дублей. Мы говорим системе: «Сначала разберись с остатками внутри каждой номенклатуры, а потом отдай результат выше».Сумма(...) — внешняя функция, которая складывает все полученные уникальные значения из массива для расчета итогов по папкам или всему отчету.Важный нюанс: при использовании этого метода важно правильно указывать поле группировки. Если считать не по полю Номенклатура, а по самому полю Остаток, система может пропустить разные товары с одинаковыми остатками, что приведет к занижению итогов.
Еще один эффективный и зачастую более «чистый» с точки зрения архитектуры способ — разделение данных на разные наборы. Вместо одного сложного запроса с ЛЕВОЕ СОЕДИНЕНИЕ, мы создаем два отдельных набора данных:
Связь между ними настраивается на вкладке «Связи наборов данных». В качестве источника связи выступает набор с остатками, приемником — набор с ценами. Условие связи — Номенклатура.
В этом случае СКД работает иначе: ресурс Количество существует только в первом наборе. При расчете итогов по группировке «Номенклатура» система берет значение из первого набора один раз, а цены из второго набора просто «пристыковываются» в детальные записи. Дублирования строк на уровне источника ресурса не происходит, и обычная Сумма(Количество) будет работать корректно.
Выясним, какой метод лучше использовать в различных ситуациях:
Также не забывайте проверять настройку ролей полей. Для поля Номенклатура должна быть указана роль «Измерение», что помогает СКД правильно идентифицировать уникальность записей при расчете итогов.