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