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

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

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

Выясним причину: почему левое соединение превращается во внутреннее

Рассмотрим стандартный порядок выполнения запроса СУБД. Когда мы пишем запрос с левым соединением, СУБД сначала формирует промежуточный результат соединения. В тех строках, где для записи из левой таблицы не нашлось пары из правой таблицы, поля правой таблицы заполняются значением NULL.

Если после этого в секции ГДЕ накладывается прямое условие на реквизит правой таблицы, например:


ГДЕ
    ПоступлениеТоваровУслугРазделыГТД.Ссылка = &Ссылка

Происходит следующее: для строк, где записи из правой таблицы отсутствовали, выражение ПоступлениеТоваровУслугРазделыГТД.Ссылка имеет значение NULL. Сравнение NULL = &Ссылка возвращает значение ЛОЖЬ (или НЕОПРЕДЕЛЕНО). В результате секция ГДЕ отсекает абсолютно все строки с NULL. Таким образом, левое соединение фактически начинает отрабатывать как ВНУТРЕННЕЕ СОЕДИНЕНИЕ.

Разберем три варианта решения этой задачи и проанализируем технические нюансы каждого подхода.

Способ 1. Перенос условий фильтрации правой таблицы в секцию ПО

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

При таком подходе фильтрация правой таблицы происходит до или в момент соединения. Если правая таблица не содержит подходящих строк, слева данные все равно остаются, а поля правой таблицы заполняются значениями NULL.

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


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

Способ 2. Использование временных таблиц

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

Проанализируем пример кода с временными таблицами:


ВЫБРАТЬ
    ПоступлениеТоваровУслугТовары.НомерСтроки КАК НомерСтроки,
    ПоступлениеТоваровУслугТовары.Номенклатура.КодТНВЭД КАК КодТНВЭД,
    ПоступлениеТоваровУслугТовары.Сумма КАК Сумма,
    ПоступлениеТоваровУслугТовары.Номенклатура КАК Номенклатура,
    ВЫБОР
        КОГДА ЕСТЬNULL(ПоступлениеТоваровУслугТовары.Вес, 0) > 0
            ТОГДА ПоступлениеТоваровУслугТовары.Вес
        ИНАЧЕ ПоступлениеТоваровУслугТовары.Количество
    КОНЕЦ КАК Количество,
    ПоступлениеТоваровУслугТовары.Цена КАК Цена,
    ПоступлениеТоваровУслугТовары.Сумма * ПоступлениеТоваровУслугТовары.КурсУпр КАК СуммаРуб
ПОМЕСТИТЬ ВТ_Товары
ИЗ
    Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
ГДЕ
    ПоступлениеТоваровУслугТовары.Ссылка = &Ссылка
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ПоступлениеТоваровУслугРазделыГТД.КодТНВЭД КАК КодТНВЭД,
    ПоступлениеТоваровУслугРазделыГТД.СуммаПошлины КАК СуммаПошлины,
    ПоступлениеТоваровУслугРазделыГТД.СуммаНДС КАК СуммаНДС
ПОМЕСТИТЬ ВТ_РазделыГТД
ИЗ
    Документ.ПоступлениеТоваровУслуг.РазделыГТД КАК ПоступлениеТоваровУслугРазделыГТД
ГДЕ
    ПоступлениеТоваровУслугРазделыГТД.Ссылка = &Ссылка
ИНИЕКСИРОВАТЬ ПО
    КодТНВЭД
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Товары.НомерСтроки КАК НомерСтроки,
    ВТ_Товары.КодТНВЭД КАК КодТНВЭД,
    ВТ_Товары.Сумма КАК Сумма,
    ВТ_Товары.Номенклатура КАК Номенклатура,
    ВТ_Товары.Количество КАК Количество,
    ВТ_Товары.Цена КАК Цена,
    ВТ_Товары.СуммаРуб КАК СуммаРуб,
    ЕСТЬNULL(ВТ_РазделыГТД.СуммаПошлины, 0) КАК СуммаПошлины,
    ЕСТЬNULL(ВТ_РазделыГТД.СуммаНДС, 0) КАК СуммаНДС
ИЗ
    ВТ_Товары КАК ВТ_Товары
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_РазделыГТД КАК ВТ_РазделыГТД
        ПО ВТ_Товары.КодТНВЭД = ВТ_РазделыГТД.КодТНВЭД
УПОРЯДОЧИТЬ ПО
    НомерСтроки
ИТОГИ
    СУММА(Сумма)
ПО
    ОБЩИЕ,
    КодТНВЭД

Оптимизация производительности и особенности платформы 1С

При выборе решения следует учитывать технические особенности работы платформы и СУБД:

  1. Избыточное разыменование Ссылка.Ссылка: В исходных запросах встречается обращение Ссылка.Ссылка. Это создает лишнюю нагрузку при трансляции запроса platform1C в SQL-код. Всегда следует писать прямое обращение к полю: Ссылка = &Ссылка.
  2. Неявные соединения через точку: Выражение ПоступлениеТоваровУслугТовары.Номенклатура.КодТНВЭД заставляет платформу автоматически создавать неявное ЛЕВОЕ СОЕДИНЕНИЕ со справочником «Номенклатура». Если это соединение используется непосредственно в блоке ПО, оптимизатору СУБД сложно построить эффективный план выполнения. При использовании временных таблиц реквизит вычисляется один раз, что упрощает итоговый план.
  3. Индексация временных таблиц: При соединении временных таблиц по полю КодТНВЭД обязательно используйте ключевое слово ИНДЕКСИРОВАТЬ ПО. Без индекса СУБД будет вынуждена выполнять полное сканирование таблицы в памяти или файле tempdb.
  4. Выбор между единственным запросом и ВТ:

    Для выборки по одному документу запрос из Способа 1 (без временных таблиц) отработает быстрее, так как отсутствует накладной расход на создание, запись и удаление временных таблиц в базе СУБД.

    Для выборки данных по большому массиву документов предпочтительнее использовать временные таблицы с явной индексацией по полям связи.

← На главную