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

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

При разработке отчетов и обработок в 1С часто возникает задача сопоставить остатки товаров на конкретном основном складе с общими остатками по сети магазинов или фирме в целом. Распространенный подход — обращение к виртуальной таблице РегистрНакопления.ТоварыНаСкладах.Остатки с наложением конструкции ЛЕВОЕ СОЕДИНЕНИЕ на эту же таблицу остатков. Однако разработчики нередко сталкиваются с ситуацией, когда позиции или характеристики, отсутствующие на подчиненных складах, пропадают и из выборки основного склада, как будто сработало внутреннее соединение. Разберем подробно, почему так происходит и как правильно строить подобные запросы.

Анализ исходной проблемы

Рассмотрим исходный запрос, в котором проявляется проблема:


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

На первый взгляд синтаксис ЛЕВОЕ СОЕДИНЕНИЕ должен гарантировать сохранение всех строк из таблицы ОстаткиСклада. Выясним основные причины, из-за которых на практике строки могут отсекаться или отображаться некорректно:

  1. Различие в характеристиках и пустых ссылках. В регистрах остатков поле Характеристика может содержать пустую ссылку определенного типа или значение NULL (в зависимости от настроек учета и структуры регистра). Если тип пустой ссылки отличается или значение не заполнено несимметрично, условие соединения не срабатывает.
  2. Декартово произведение и дублирование строк. Если один и тот же товар с одинаковой характеристикой числится на пяти разных складах магазинов, правая таблица вернет пять строк на одну строку левой таблицы. Без агрегации и группировки остаток склада будет неконтролируемо дублироваться.
  3. Неявные отборы и разыменование ссылок. Использование поля ОстаткиМагазина.Склад.Магазин приводит к неявному левому соединению со справочником складов. Если в дальнейшем к этому запросу в СКД или коде накладывается условие в секции ГДЕ по полям правой таблицы, все записи со значением NULL автоматически отсекаются СУБД, превращая левое соединение во внутреннее.
  4. Отсутствие обработки NULL. Для строк, которых нет в правой таблице, поле ОстатокМагазина вернет NULL. При математических операциях или выгрузке в коллекции это может приводить к ошибкам или визуальному отсутствию данных.

Способ 1. Использование временных таблиц с предварительной группировкой

Чтобы избежать размножения строк и исключить влияние детальных разрезов правой таблицы, разделим получение данных на временные таблицы с явной агрегацией.

Разберем по шагам реализацию через менеджер временных таблиц:

  1. Выбираем остатки только по целевому основному складу и помещаем их в таблицу ВТ_ОстаткиСклада.
  2. Выбираем остатки по остальным складам, группируем их по номенклатуре и характеристике, суммируя количество, и помещаем во ВТ_ОстаткиМагазинов.
  3. Выполняем левое соединение первой таблицы со второй с обязательной обработкой значений через ЕСТЬNULL.

ВЫБРАТЬ
    Остатки.Номенклатура КАК Номенклатура,
    Остатки.Характеристика КАК Характеристика,
    Остатки.КоличествоОстаток КАК ОстатокСклада
ПОМЕСТИТЬ ВТ_ОстаткиСклада
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(, Склад = &Склад) КАК Остатки
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Остатки.Номенклатура КАК Номенклатура,
    Остатки.Характеристика КАК Характеристика,
    СУММА(Остатки.КоличествоОстаток) КАК ОстатокМагазинов
ПОМЕСТИТЬ ВТ_ОстаткиМагазинов
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(, Склад <> &Склад) КАК Остатки
СГРУППИРОВАТЬ ПО
    Остатки.Номенклатура,
    Остатки.Характеристика
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_ОстаткиСклада.Номенклатура КАК Номенклатура,
    ВТ_ОстаткиСклада.Характеристика КАК Характеристика,
    ВТ_ОстаткиСклада.ОстатокСклада КАК ОстатокСклада,
    ЕСТЬNULL(ВТ_ОстаткиМагазинов.ОстатокМагазинов, 0) КАК ОстатокМагазинов
ИЗ
    ВТ_ОстаткиСклада КАК ВТ_ОстаткиСклада
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ОстаткиМагазинов КАК ВТ_ОстаткиМагазинов
        ПО ВТ_ОстаткиСклада.Номенклатура = ВТ_ОстаткиМагазинов.Номенклатура
            И ВТ_ОстаткиСклада.Характеристика = ВТ_ОстаткиМагазинов.Характеристика

Способ 2. Оптимальное решение: условное суммирование в одном обращении к регистру

Соединение виртуальной таблицы остатков самой с собой — ресурсоемкая операция, создающая избыточную нагрузку на СУБД. Наиболее производительный и надежный подход в платформе 1С — выборка остатков в рамках одного запроса без каких-либо соединений с разделением по складам через конструкцию ВЫБОР.

Посмотрим на пример такого запроса:


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

Проанализируем преимущества данного подхода:

  1. Гарантия целостности строк: отсутствие соединений полностью исключает риск потери характеристик из-за несовпадения ключей связи.
  2. Высокое быстродействие: чтение данных из виртуальной таблицы остатков происходит только один раз.
  3. Фильтрация через секцию ИМЕЮЩИЕ: условие позволяет оставить в выборке только те позиции, которые гарантированно присутствуют на основном складе, не теряя информацию об их остатках в магазинах.
← На главную