При разработке отчетов в системе компоновки данных (СКД) разработчики часто сталкиваются с ситуацией, когда числовые показатели (суммы оборотов, себестоимость, закупки) необоснованно увеличиваются в несколько раз. Разберем типичный пример: в запросе выполняется левое соединение табличной части документа продаж с виртуальной таблицей оборотов регистра накопления. В результате сумма прихода умножается на количество строк продаж по каждой номенклатуре. Выясним причину такого поведения и рассмотрим правильные варианты построения отчета.
Посмотрим на исходный запрос, приводящий к ошибке:
ВЫБРАТЬ
ПродажаТоваровТовары.Номенклатура КАК Номенклатура,
ПродажаТоваровТовары.Количество КАК Количество,
ПродажаТоваровТовары.Сумма КАК Сумма,
ОстаткиНоменклатурыОбороты.СуммаПриход КАК Приход
ИЗ
Документ.ПродажаТоваров.Товары КАК ПродажаТоваровТовары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры.Обороты(&Дата1, &Дата2, , ) КАК ОстаткиНоменклатурыОбороты
ПО ПродажаТоваровТовары.Номенклатура = ОстаткиНоменклатурыОбороты.Номенклатура
ГДЕ
ПродажаТоваровТовары.Ссылка.Дата МЕЖДУ &Дата1 И &Дата2
И ПродажаТоваровТовары.Ссылка.Проведен
Проанализируем механизм возникновения задвоения сумм:
ОстаткиНоменклатуры.Обороты возвращает одну свернутую строку по каждой позиции номенклатуры.ЛЕВОЕ СОЕДИНЕНИЕ итоговое значение оборота из регистра привязывается к каждой строке документа. Если товар продавался 5 раз, оборот прихода продублируется в 5 строках выборки.СУММА(Приход) по группировке Номенклатура, она складывает все строки детальных записей, из-за чего значение оборота умножается на число строк продаж.Если по условиям задачи требуется связать продажи из документов с регистром через соединение, необходимо предварительно сгруппировать данные документов по ключевому полю (номенклатуре). Разберем по шагам реализацию через пакетный запрос:
СГРУППИРОВАТЬ ПО и помещаем во временную таблицу.ИНДЕКСИРОВАТЬ ПО для ускорения выборки.Посмотрим на пример пакетного запроса:
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
СУММА(Продажи.Количество) КАК Количество,
СУММА(Продажи.Сумма) КАК Сумма
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
Документ.ПродажаТоваров.Товары КАК Продажи
ГДЕ
Продажи.Ссылка.Дата МЕЖДУ &Дата1 И &Дата2
И Продажи.Ссылка.Проведен
СГРУППИРОВАТЬ ПО
Продажи.Номенклатура
ИНДЕКСИРОВАТЬ ПО
Номенклатура
;
ВЫБРАТЬ
ВТ_Продажи.Номенклатура КАК Номенклатура,
ВТ_Продажи.Количество КАК Количество,
ВТ_Продажи.Сумма КАК Сумма,
ЕСТЬNULL(Обороты.СуммаПриход, 0) КАК Приход
ИЗ
ВТ_Продажи КАК ВТ_Продажи
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры.Обороты(&Дата1, &Дата2, , ) КАК Обороты
ПО ВТ_Продажи.Номенклатура = Обороты.Номенклатура
Обратим внимание на функцию ЕСТЬNULL(Обороты.СуммаПриход, 0): если по проданной номенклатуре в выбранном периоде не было движений прихода, вместо значения NULL будет подставлен корректный ноль.
Классическим и наиболее производительным решением в 1С для сведения показателей из разных источников является использование конструкции ОБЪЕДИНИТЬ ВСЕ с последующей группировкой. Это позволяет избежать соединений и исключает риск декартова произведения таблиц.
ВЫБРАТЬ
Т.Номенклатура КАК Номенклатура,
СУММА(Т.Количество) КАК Количество,
СУММА(Т.Сумма) КАК Сумма,
СУММА(Т.Приход) КАК Приход
ИЗ
(ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
Продажи.Количество КАК Количество,
Продажи.Сумма КАК Сумма,
0 КАК Приход
ИЗ
Документ.ПродажаТоваров.Товары КАК Продажи
ГДЕ
Продажи.Ссылка.Дата МЕЖДУ &Дата1 И &Дата2
И Продажи.Ссылка.Проведен
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Обороты.Номенклатура,
0,
0,
Обороты.СуммаПриход
ИЗ
РегистрНакопления.ОстаткиНоменклатуры.Обороты(&Дата1, &Дата2, , ) КАК Обороты) КАК Т
СГРУППИРОВАТЬ ПО
Т.Номенклатура
Преимущества такого подхода: запрос работает быстро, не требует индексации временных таблиц при небольших выборках и корректно включает номенклатуру, по которой были только закупки или только продажи.
Система компоновки данных позволяет разделить выборку данных на два независимых набора, исключив дублирование на уровне механизма компоновки:
РегистрНакопления.ОстаткиНоменклатуры.Обороты.НаборДанных1, приемник — НаборДанных2, условие связи — Номенклатура = Номенклатура.СКД выполнит связывание данных на уровне группировок без искажения итоговых сумм.
Если изменить исходный запрос нельзя, можно скорректировать формулу расчета ресурса на вкладке «Ресурсы» в СКД. Для поля Приход в выражении ресурса используем специальную функцию языка выражений СКД:
Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(Приход)", "Номенклатура"))
Это выражение сначала получает единственное максимальное значение прихода по каждой уникальной номенклатуре, сворачивает их в массив и затем суммирует элементы массива на верхних уровнях группировок и в общем итоге.
При построении отчетов важно соблюдать базовые стандарты разработки 1С:
Продажи.Обороты).РегистрНакопления.ОстаткиНоменклатуры.Обороты(&Дата1, &Дата2, , ). Установка условий на поля виртуальных таблиц в секции ГДЕ приводит к чтению избыточных данных из базы и падению скорости отчета.