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