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

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

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

В чем суть проблемы и почему «плывут» итоги?

Представим типовую бизнес-логику: на производстве выпускается готовая продукция. Данные фиксируются в регистре накопления. При проведении документа выпуска в регистр записывается информация о произведенном товаре и одновременно о сырье (комплектующих), которое ушло на его изготовление. Таким образом, на одну единицу выпущенной продукции приходится несколько строк списанного сырья (классическая связь «один-ко-многим»).

Если мы попытаемся соединить эти данные внутри одного запроса, СУБД выполнит операцию LEFT JOIN или INNER JOIN. На уровне строк это будет выглядеть следующим образом:

  1. Выпущено: Шкаф деревянный — 1 шт.
  2. Потрачено сырья: Доска (3 шт.), Шурупы (10 шт.), Лак (1 л).
  3. В результате соединения мы получим 3 строки, в каждой из которых будет указано: выпуск — 1 шт., а сырье — соответствующее расходу.

Когда этот запрос выполняется в консоли с жесткой группировкой по продукции, мы можем применить функцию МАКСИМУМ к количеству продукции, и итог покажет правильное значение — 1 шт. Но как только мы переносим этот запрос в СКД и пользователь выводит детальные записи (включая сырье и регистратор), СКД динамически перестраивает структуру запроса. При попытке рассчитать общие итоги по ресурсу «Количество продукции» СКД выполнит стандартное суммирование Сумма(КоличествоПродукции) по всем строкам выборки. В результате наш единственный выпущенный шкаф просуммируется трижды, и в отчете мы увидим выпуск в объеме 3 шт. вместо 1 шт. Это и есть классическое «задвоение» или размножение итогов.

Давайте разберем по шагам, как решить эту проблему профессиональными методами разработки.

Способ 1. Разделение запроса на два набора данных и их связывание в СКД

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

Мы создадим два независимых набора данных типа «Запрос».

Шаг 1. Создаем первый набор данных. Назовем его НаборДанныхВыпуск. Этот запрос будет выбирать исключительно уникальные данные по выпуску продукции без какой-либо информации о сырье.


ВЫБРАТЬ
    ОборотыПродукции.Регистратор КАК Регистратор,
    ОборотыПродукции.Продукция КАК Продукция,
    ОборотыПродукции.КоличествоПродукцииОборот КАК КоличествоПродукции
ИЗ
    РегистрНакопления.ОборотПродукции.Обороты(,, Авто) КАК ОборотыПродукции

Шаг 2. Создаем второй набор данных. Назовем его НаборДанныхСырье. Здесь мы будем выбирать только данные по списанию материалов, не затрагивая количественные показатели самого выпуска.


ВЫБРАТЬ
    ОборотыСырья.Регистратор КАК Регистратор,
    ОборотыСырья.Продукция КАК Продукция,
    ОборотыСырья.Сырье КАК Сырье,
    ОборотыСырья.КоличествоСырьяОборот КАК КоличествоСырья
ИЗ
    РегистрНакопления.ОборотПродукции.Обороты(,, Авто) КАК ОборотыСырья

Шаг 3. Связываем наборы данных. Переходим на вкладку «Связи наборов данных» в схеме компоновки данных. Добавляем новую строку связи:

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

Способ 2. Использование функций агрегации массивов в ресурсах СКД (без изменения запроса)

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

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

Пропишем в поле «Выражение» для ресурса КоличествоПродукции следующую конструкцию:


Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(КоличествоПродукции)", "Регистратор, Продукция"))

Разберем детально, как работает эта формула:

  1. Для любой группировки, которая выше по иерархии (например, Склад или Общий итог), СКД временно распределяет строки во внутренний массив по уникальным комбинациям полей Регистратор и Продукция.
  2. Внутри каждой такой уникальной комбинации (то есть для конкретной строки документа выпуска конкретного товара) СКД вычисляет Максимум(КоличествоПродукции). Это мгновенно «схлопывает» все дублирующие строки сырья, оставляя только одну уникальную цифру выпуска.
  3. После этого внешняя функция Сумма складывает полученные уникальные максимальные значения. Цифры сходятся идеально на любом уровне группировки!

Способ 3. Тонкая настройка расчета ресурсов по группировкам

Если структура вашего отчета жестко фиксирована и пользователи не будут менять её на ходу в режиме «1С:Предприятие», можно применить метод разграничения расчета ресурсов. Этот способ требует аккуратности, но он очень эффективен в простых линейных отчетах.

Переходим на вкладку «Ресурсы» и добавляем поле КоличествоПродукции дважды с разными формулами:

  1. Строка 1: Выражение — Максимум(КоличествоПродукции). В колонке «Рассчитывать по...» выбираем только те детальные группировки, где выпуск гарантированно уникален в разрезе записей (например, по группировкам Регистратор и Продукция).
  2. Строка 2: Выражение — Сумма(КоличествоПродукции). В колонке «Рассчитывать по...» выбираем только вышестоящие уровни иерархии (например, Склад, Организация, Период) или оставляем поле пустым для автоматического расчета общих итогов.

Таким образом, на уровне конкретного документа мы берем максимальное (корректное) значение выпуска, а на верхних уровнях система аккуратно складывает эти локальные максимумы, минуя детальные записи сырья.

Способ 4. Отключение автозаполнения и ручное определение ролей полей

По умолчанию СКД использует механизм «Автозаполнение». Это значит, что система сама решает, как оптимизировать запрос при наложении отборов и группировок. Именно эта автоматическая оптимизация часто приводит к некорректному поведению при связях таблиц.

Мы можем отключить автозаполнение и настроить роли полей вручную:

  1. На вкладке «Наборы данных» снимаем флаг «Автозаполнение».
  2. Для полей таблицы выпуска (Продукция, КоличествоПродукции) в свойствах полей указываем их роли.
  3. Для поля Регистратор задаем роль «Период» или ключевое поле, чтобы система понимала границы уникальности записей.
  4. Для полей сырья задаем роль обязательных связей, чтобы они не влияли на подсчет итогов по независимым группировкам выпуска.

Этот способ требует глубокого понимания логики работы компоновщика, но он дает разработчику абсолютный контроль над формируемым SQL-запросом к СУБД.

Резюме и рекомендации

Мы рассмотрели четыре профессиональных подхода к решению проблемы задвоения данных в СКД. Какой из них выбрать в реальной практике?

Успешной вам разработки и только верных итогов в ваших отчетах!

← На главную