Почему дублируются записи и умножаются остатки в запросе 1С

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

При построении отчетов по регистрам накопления разработчики часто сталкиваются с ситуацией, когда данные в результирующей таблице начинают многократно дублироваться, а начальные и конечные остатки увеличиваются в разы. Особенно ярко эта проблема проявляется при попытке получить одновременно остатки и детальные обороты в разрезе документов движения (регистраторов). Проанализируем, почему возникает такое поведение платформы 1С и как правильно составить запрос и обработать его результат.

Причина 1. Суммирование остаточных полей при обращении к регистратору

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

Посмотрим на ошибочный фрагмент запроса:


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

В данном примере функция СУММА() применяется к полям остатков. Так как в выборку попадает регистратор, виртуальная таблица формирует строку остатка на каждую запись движения. Последующая группировка СГРУППИРОВАТЬ ПО складывает эти остатки между собой. В результате начальный и конечный остатки умножаются на количество документов, совершивших движения в заданном периоде.

Главное правило: поля КоличествоНачальныйОстаток и КоличествоКонечныйОстаток нельзя складывать функцией СУММА() по временной шкале.

Причина 2. Ошибки программного обхода иерархических итогов

Использование секции ИТОГИ ПО ... ИЕРАРХИЯ добавляет в выборку агрегированные строки по папкам справочника и общим итогам. Если программная выборка строк построена некорректно, детальные записи смешиваются с итогами, создавая иллюзию дублирования строк.

Разберем ошибочный вариант выгрузки:


ВыборкаНоменклатура = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаНоменклатура.Следующий() Цикл
	Если ВыборкаНоменклатура.ТипЗаписи() = ТипЗаписиЗапроса.ИтогПоИерархии Тогда
		Область = ОбластьНоменклатураИерархия;
	Иначе
		Область = ОбластьНоменклатура;
	КонецЕсли;
	ТабДок.Вывести(Область, ВыборкаНоменклатура.Уровень());
	
	// Ошибка: выборка без параметров на уровне группировки
	ВыборкаДетальныеЗаписи = ВыборкаНоменклатура.Выбрать();
	Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
		ТабДок.Вывести(ОбластьДетальныхЗаписей, ВыборкаДетальныеЗаписи.Уровень());
	КонецЦикла;
КонецЦикла;

Чтобы иерархия и детальные записи выводились строго по уровням без повторов, в метод Выбрать() необходимо передавать имя группировки и тип обхода:


ВыборкаИерархия = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Номенклатура", "ИЕРАРХИЯ");
Пока ВыборкаИерархия.Следующий() Цикл
	// Вывод строк иерархии (папок и элементов)
	ТабДок.Вывести(ОбластьИерархии, ВыборкаИерархия.Уровень());
	
	// Получение детальных записей только для текущей ветки
	ВыборкаДетали = ВыборкаИерархия.Выбрать(ОбходРезультатаЗапроса.Прямой);
	Пока ВыборкаДетали.Следующий() Цикл
		ТабДок.Вывести(ОбластьДеталей, ВыборкаДетали.Уровень());
	КонецЦикла;
КонецЦикла;

Способы решения проблемы

  1. Использование Системы компоновки данных (СКД): СКД самостоятельно рассчитывает роли остаточных полей и корректно вычисляет начальные и конечные остатки на границах периодов, исключая их суммирование при детализации по документам.
  2. Разделение запроса на таблицы остатков и оборотов: если отчет формируется кодом без СКД, остатки на начало и конец периода следует получать из таблицы Остатки, а движения документов — из таблицы Обороты или ОстаткиИОбороты без выбора полей остатка. Затем таблицы объединяются через ОБЪЕДИНИТЬ ВСЕ или соединяются по измерениям.
  3. Перенос условий в параметры виртуальной таблицы: фильтрацию по складу и номенклатуре необходимо переносить из блока ГДЕ в параметры виртуальной таблицы. Это снижает объем обрабатываемых данных и ускоряет работу СУБД:
    
    РегистрНакопления.ПартииТоваровНаСкладах.ОстаткиИОбороты(
    	&ДатаН, 
    	&ДатаК, 
    	Авто, 
    	, 
    	Склад = &Склад И Номенклатура В ИЕРАРХИИ (&Номенклатура)
    ) КАК ПартииТоваровНаСкладахОстаткиИОбороты
    
  4. Отказ от избыточных инструкций: совместное использование ВЫБРАТЬ РАЗЛИЧНЫЕ и СГРУППИРОВАТЬ ПО является избыточным, так как группировка уже обеспечивает уникальность записей по указанным полям.
← На главную