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