Почему некорректно считаются остатки в СКД при использовании соединений и как это исправить

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

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

Причина возникновения проблемы: механизм работы СКД

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

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

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

Решение №1: Использование необязательных связей (Фигурные скобки)

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


ВЫБРАТЬ
    ОстаткиНоменклатурыОстаткиИОбороты.Регистратор КАК Регистратор,
    ОстаткиНоменклатурыОстаткиИОбороты.ПериодСекунда КАК ПериодСекунда,
    ОстаткиНоменклатурыОстаткиИОбороты.КоличествоНачальныйОстаток КАК КоличествоНачальныйОстаток,
    ОстаткиНоменклатурыОстаткиИОбороты.КоличествоПриход КАК КоличествоПриход,
    ОстаткиНоменклатурыОстаткиИОбороты.КоличествоРасход КАК КоличествоРасход,
    ОстаткиНоменклатурыОстаткиИОбороты.КоличествоКонечныйОстаток КАК КоличествоКонечныйОстаток,
    РасходнаяНакладная.Дата КАК Дата,
    ОстаткиНоменклатурыОстаткиИОбороты.Номенклатура КАК Номенклатура
ИЗ
    РегистрНакопления.ОстаткиНоменклатуры.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Авто, , ) КАК ОстаткиНоменклатурыОстаткиИОбороты
    {ЛЕВОЕ СОЕДИНЕНИЕ Документ.РасходнаяНакладная КАК РасходнаяНакладная
    ПО ОстаткиНоменклатурыОстаткиИОбороты.Регистратор = РасходнаяНакладная.Ссылка}

Обратите внимание на использование фигурных скобок {} вокруг оператора соединения. Это указывает СКД, что данную таблицу нужно подключать только в том случае, если поля из неё (в нашем примере это Дата) выбраны пользователем в настройках отчета. Если поле не выбрано, СКД полностью исключит соединение из итогового SQL-запроса, и расчет остатков пройдет в штатном режиме.

Решение №2: Правильная настройка ролей полей

Если соединение необходимо всегда, выясним, как настроить роли полей на закладке "Наборы данных". Для корректного расчета остатков необходимо выполнить следующие шаги:

  1. Для полей КоличествоНачальныйОстаток и КоличествоКонечныйОстаток откройте колонку "Роль".
  2. Убедитесь, что для обоих полей указано одинаковое Имя группы (например, "Количество").
  3. В поле Тип для начального остатка выберите НачальныйОстаток, для конечного — КонечныйОстаток.
  4. В поле Поле периода обязательно укажите основное поле периода (например, ПериодСекунда). Это критически важно: СКД должна знать, по какой временной шкале "раскладывать" эти значения.
  5. Проверьте Игнорировать значения NULL. При левом соединении могут возникать пустые значения, которые могут сбивать алгоритм расчета итогов.

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

Решение №3: Разделение на разные наборы данных

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

  1. НаборДанных1 (Запрос): Получает данные только из виртуальной таблицы ОстаткиИОбороты со всеми необходимыми измерениями и ресурсами.
  2. НаборДанных2 (Запрос): Получает дополнительные сведения (реквизиты документов, справочников и т.д.).

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

Решение №4: Особенности кэширования метаданных

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

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

← На главную