Как устранить дублирование сумм в отчете СКД при соединении табличной части документа с регистром

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

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

Анализ исходного запроса и причины проблемы

Посмотрим на исходный запрос, приводящий к ошибке:


ВЫБРАТЬ
    ПродажаТоваровТовары.Номенклатура КАК Номенклатура,
    ПродажаТоваровТовары.Количество КАК Количество,
    ПродажаТоваровТовары.Сумма КАК Сумма,
    ОстаткиНоменклатурыОбороты.СуммаПриход КАК Приход
ИЗ
    Документ.ПродажаТоваров.Товары КАК ПродажаТоваровТовары
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры.Обороты(&Дата1, &Дата2, , ) КАК ОстаткиНоменклатурыОбороты
    ПО ПродажаТоваровТовары.Номенклатура = ОстаткиНоменклатурыОбороты.Номенклатура
ГДЕ
    ПродажаТоваровТовары.Ссылка.Дата МЕЖДУ &Дата1 И &Дата2
    И ПродажаТоваровТовары.Ссылка.Проведен

Проанализируем механизм возникновения задвоения сумм:

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

Способ 1. Использование временных таблиц и предварительной группировки (решение через пакетный запрос)

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

  1. Выбираем продажи из табличной части документов, сворачиваем их с помощью СГРУППИРОВАТЬ ПО и помещаем во временную таблицу.
  2. Индексируем поле соединения с помощью конструкции ИНДЕКСИРОВАТЬ ПО для ускорения выборки.
  3. Вторым запросом соединяем временную таблицу с виртуальной таблицей регистра накопления с равной детальностью (одна строка на одну номенклатуру).

Посмотрим на пример пакетного запроса:


ВЫБРАТЬ
    Продажи.Номенклатура КАК Номенклатура,
    СУММА(Продажи.Количество) КАК Количество,
    СУММА(Продажи.Сумма) КАК Сумма
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
    Документ.ПродажаТоваров.Товары КАК Продажи
ГДЕ
    Продажи.Ссылка.Дата МЕЖДУ &Дата1 И &Дата2
    И Продажи.Ссылка.Проведен
СГРУППИРОВАТЬ ПО
    Продажи.Номенклатура
ИНДЕКСИРОВАТЬ ПО
    Номенклатура
;

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

Обратим внимание на функцию ЕСТЬNULL(Обороты.СуммаПриход, 0): если по проданной номенклатуре в выбранном периоде не было движений прихода, вместо значения NULL будет подставлен корректный ноль.

Способ 2. Архитектурный шаблон через объединение выборок (ОБЪЕДИНИТЬ ВСЕ)

Классическим и наиболее производительным решением в 1С для сведения показателей из разных источников является использование конструкции ОБЪЕДИНИТЬ ВСЕ с последующей группировкой. Это позволяет избежать соединений и исключает риск декартова произведения таблиц.


ВЫБРАТЬ
    Т.Номенклатура КАК Номенклатура,
    СУММА(Т.Количество) КАК Количество,
    СУММА(Т.Сумма) КАК Сумма,
    СУММА(Т.Приход) КАК Приход
ИЗ
    (ВЫБРАТЬ
        Продажи.Номенклатура КАК Номенклатура,
        Продажи.Количество КАК Количество,
        Продажи.Сумма КАК Сумма,
        0 КАК Приход
    ИЗ
        Документ.ПродажаТоваров.Товары КАК Продажи
    ГДЕ
        Продажи.Ссылка.Дата МЕЖДУ &Дата1 И &Дата2
        И Продажи.Ссылка.Проведен
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ
        Обороты.Номенклатура,
        0,
        0,
        Обороты.СуммаПриход
    ИЗ
        РегистрНакопления.ОстаткиНоменклатуры.Обороты(&Дата1, &Дата2, , ) КАК Обороты) КАК Т

СГРУППИРОВАТЬ ПО
    Т.Номенклатура

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

Способ 3. Использование связей наборов данных в СКД

Система компоновки данных позволяет разделить выборку данных на два независимых набора, исключив дублирование на уровне механизма компоновки:

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

СКД выполнит связывание данных на уровне группировок без искажения итоговых сумм.

Способ 4. Расчет ресурса с помощью функций СКД

Если изменить исходный запрос нельзя, можно скорректировать формулу расчета ресурса на вкладке «Ресурсы» в СКД. Для поля Приход в выражении ресурса используем специальную функцию языка выражений СКД:

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

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

Важные рекомендации по производительности

При построении отчетов важно соблюдать базовые стандарты разработки 1С:

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