Почему умножаются суммы в отчете СКД и как это исправить

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

При разработке отчетов на базе Системы компоновки данных (СКД) программисты часто сталкиваются с неприятной ситуацией: итоговые суммы в колонках начинают «раздуваться», умножаясь в несколько раз. Например, вместо ожидаемых 500 000 рублей в отчете выводится 2 000 000. Рассмотрим подробно, почему это происходит, проанализируем конкретный пример кода и разберем правильные методы решения этой проблемы.

Выясняем причину: почему суммы «размножаются»?

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

Проанализируем ситуацию на примере залоговых билетов. Допустим, у нас есть один залоговый билет на сумму 1000 рублей. Мы хотим вывести в отчет его статус. Если в таблице статусов для этого билета по ошибке или в силу логики данных окажется две записи (например, статус «Активен» и признак «Лояльный клиент»), то после соединения ЛЕВОЕ СОЕДИНЕНИЕ система создаст две строки. В каждой из этих строк будет стоять сумма 1000 рублей. При расчете итогов СКД просто сложит эти значения, и мы получим 2000 рублей вместо 1000.

Анализ исходного запроса

Посмотрим на фрагмент запроса, который вызвал проблему у автора темы. В нем формируется временная таблица вт_Статусы с помощью оператора ОБЪЕДИНИТЬ ВСЕ:


ВЫБРАТЬ
    ломб_СхемыКредитования.Контрагент.ИдентификационныйКодЛичности КАК ИИН,
    "✔" КАК Индивидуальная,
    "" КАК Лояльные,
    "" КАК ЧерныйСписок
ПОМЕСТИТЬ вт_Статусы
ИЗ
    Справочник.ломб_СхемыКредитования КАК ломб_СхемыКредитования
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
    ломб_СписокЗапрещенныхФизлиц.ИИН, "", "", "✔"
ИЗ
    Справочник.ломб_СписокЗапрещенныхФизлиц КАК ломб_СписокЗапрещенныхФизлиц
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
    ломб_РазрешенныеСотрудникиЛьготноеКредитование.Сотрудник.ИдентификационныйКодЛичности, "", "✔", ""
ИЗ
    РегистрСведений.ломб_РазрешенныеСотрудникиЛьготноеКредитование КАК ломб_РазрешенныеСотрудникиЛьготноеКредитование

Проблема здесь очевидна: один и тот же человек (с одним ИИН) может одновременно находиться в справочнике схем кредитования, в списке запрещенных лиц и быть сотрудником. Использование ОБЪЕДИНИТЬ ВСЕ создает для такого контрагента три разные строки в таблице вт_Статусы. Когда в финальном запросе происходит соединение по полю ИИН, каждая сумма по этому контрагенту утраивается.

Решение 1: Использование ключевого слова БЕЗ ПОВТОРЯЮЩИХСЯ

Самый быстрый способ, к которому прибегнул автор темы, — добавление БЕЗ ПОВТОРЯЮЩИХСЯ (DISTINCT) в итоговом запросе. Рассмотрим, как это выглядит:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    вт_КонтрРазрешенные.ЗалоговыйБилетКонтрагент,
    вт_КонтрРазрешенные.ЗалоговыйБилетСуммаКредита,
    вт_Статусы.ИндивидуальныйКлиент,
    ...

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

Решение 2: Предварительная агрегация данных (Рекомендуемый метод)

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


ВЫБРАТЬ
    Т.ИИН КАК ИИН,
    МАКСИМУМ(Т.Индивидуальная) КАК Индивидуальная,
    МАКСИМУМ(Т.Лояльные) КАК Лояльные,
    МАКСИМУМ(Т.ЧерныйСписок) КАК ЧерныйСписок
ПОМЕСТИТЬ вт_Статусы_Сгруппированная
ИЗ (
    ВЫБРАТЬ ИИН, Индивидуальная, Лояльные, ЧерныйСписок ИЗ вт_Статусы
) КАК Т
СГРУППИРОВАТЬ ПО
    Т.ИИН;

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

Решение 3: Проверка уникальности в виртуальных таблицах

Иногда суммы множатся еще до всяких соединений. Проанализируем использование виртуальной таблицы СрезПоследних. Если регистр сведений, из которого мы берем данные, имеет несколько измерений (например, Контрагент и Организация), а мы делаем срез только по одному из них, система может вернуть несколько записей.

Всегда проверяйте, что условия в параметрах виртуальных таблиц и группировки в первом запросе (вт_ЗБ в примере автора) действительно обеспечивают уникальность ключа (например, ЗалоговыйБилет). Если один билет попадает в результат дважды из-за разных статусов в регистре, суммы «поплывут» уже на этом этапе.

Решение 4: Инструменты СКД для борьбы с дублями

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

  1. Настройка ролей полей: В наборе данных можно указать для поля ЗалоговыйБилет роль «Без дублей» или явно определить его как ключевое поле. В некоторых случаях СКД сможет корректно рассчитать итоги, понимая иерархию данных.
  2. Выражение ресурса: Вместо простой функции Сумма(СуммаКредита) можно использовать более сложную формулу на языке выражений СКД: Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(СуммаКредита)", "ЗалоговыйБилет")). Это заставит систему сначала найти максимум суммы для каждого уникального билета, а затем сложить эти максимумы.

Методика отладки: как найти «виновника»?

Если вы столкнулись с умножением сумм в сложном отчете, выполните следующие шаги для диагностики:

  1. Метод исключения: Последовательно комментируйте ЛЕВОЕ СОЕДИНЕНИЕ с каждой таблицей. Если после отключения очередной таблицы сумма стала правильной — значит, именно в ней содержатся дубли по ключевому полю.
  2. Проверка на количество: Добавьте в запрос временное поле Количество(*) и сгруппируйте данные по идентификатору (например, по ссылке на документ). Если в результате выполнения запроса вы видите число больше 1 напротив одного документа — вы нашли причину раздувания итогов.
  3. Проверка ИИН/Кодов: Убедитесь, что в справочнике Контрагенты нет дублей. Часто бывает так, что заведены два разных элемента с одинаковым ИИН. В этом случае соединение по ИИН гарантированно даст удвоение строк.

Подведем итог: для получения корректных данных в СКД необходимо обеспечивать уникальность записей в присоединяемых таблицах. Использование БЕЗ ПОВТОРЯЮЩИХСЯ допустимо для простых задач, но правильным архитектурным решением является предварительная группировка аналитических данных во временных таблицах по уникальному ключу связи.

← На главную