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

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

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

Причина: превращение левого соединения во внутреннее через секцию ГДЕ

Проанализируем фрагмент запроса, в котором возникла проблема:


ВЫБРАТЬ
    ВТ_МинусовыеОстатки.Номенклатура КАК Номенклатура,
    ЕСТЬNULL(СборкаТоваров.Ссылка, НЕОПРЕДЕЛЕНО) КАК ДокСборка,
    ВТ_МинусовыеОстатки.СкладОтправитель КАК СкладОтправитель
ИЗ
    ВТ_МинусовыеОстатки КАК ВТ_МинусовыеОстатки
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.СборкаТоваров КАК СборкаТоваров
        ПО ВТ_МинусовыеОстатки.СкладОтправитель = СборкаТоваров.Склад
            И ВТ_МинусовыеОстатки.Номенклатура = СборкаТоваров.Номенклатура
ГДЕ
    СборкаТоваров.Проведен
    И СборкаТоваров.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийКомплектацияНоменклатуры.Комплектация)
    И СборкаТоваров.Дата МЕЖДУ НАЧАЛОПЕРИОДА(&ДатаДокаПеремещения, ДЕНЬ) И &ДатаДокаПеремещения

Выясним причину такого поведения. Механизм соединения таблиц отрабатывает поэтапно:

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

Таким образом, любое строгое условие на поля правой таблицы в секции ГДЕ фактически превращает ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ.

Решение 1. Перенос условий фильтрации в секцию ПО

Если необходимо сохранить все строки левой таблицы, а правую присоединять только при совпадении критериев, перенесем отборы по правой таблице непосредственно в условие соединения ПО.

Посмотрим на пример реализации:


ВЫБРАТЬ
    ВТ_МинусовыеОстатки.Номенклатура КАК Номенклатура,
    ЕСТЬNULL(СборкаТоваров.Ссылка, ЗНАЧЕНИЕ(Документ.СборкаТоваров.ПустаяСсылка)) КАК ДокСборка,
    ВТ_МинусовыеОстатки.СкладОтправитель КАК СкладОтправитель
ИЗ
    ВТ_МинусовыеОстатки КАК ВТ_МинусовыеОстатки
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.СборкаТоваров КАК СборкаТоваров
        ПО ВТ_МинусовыеОстатки.СкладОтправитель = СборкаТоваров.Склад
            И ВТ_МинусовыеОстатки.Номенклатура = СборкаТоваров.Номенклатура
            И СборкаТоваров.Проведен
            И СборкаТоваров.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийКомплектацияНоменклатуры.Комплектация)
            И СборкаТоваров.Дата МЕЖДУ НАЧАЛОПЕРИОДА(&ДатаДокаПеремещения, ДЕНЬ) И &ДатаДокаПеремещения

В этом случае отбор применяется к правой таблице до момента объединения строк, и если подходящий проведенный документ не найден, строка левой таблицы останется в результирующем наборе.

Решение 2. Подготовка правой таблицы во временную таблицу (рекомендуемый подход)

Более надежным и читаемым решением в платформе 1С является предварительный сбор данных правой таблицы в отдельную временную таблицу с наложением всех условий, и последующее соединение уже подготовленных данных.

Разберем по шагам создание временной таблицы:


ВЫБРАТЬ
    СборкаТоваров.Склад КАК Склад,
    СборкаТоваров.Номенклатура КАК Номенклатура,
    МАКСИМУМ(СборкаТоваров.Ссылка) КАК Ссылка
ПОМЕСТИТЬ ВТ_СуществующиеСборки
ИЗ
    Документ.СборкаТоваров КАК СборкаТоваров
ГДЕ
    СборкаТоваров.Проведен
    И СборкаТоваров.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийКомплектацияНоменклатуры.Комплектация)
    И СборкаТоваров.Дата МЕЖДУ НАЧАЛОПЕРИОДА(&ДатаДокаПеремещения, ДЕНЬ) И &ДатаДокаПеремещения
СГРУППИРОВАТЬ ПО
    СборкаТоваров.Склад,
    СборкаТоваров.Номенклатура
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_МинусовыеОстатки.Номенклатура КАК Номенклатура,
    ВТ_МинусовыеОстатки.СкладОтправитель КАК СкладОтправитель,
    ЕСТЬNULL(ВТ_СуществующиеСборки.Ссылка, ЗНАЧЕНИЕ(Документ.СборкаТоваров.ПустаяСсылка)) КАК ДокСборка
ИЗ
    ВТ_МинусовыеОстатки КАК ВТ_МинусовыеОстатки
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СуществующиеСборки КАК ВТ_СуществующиеСборки
        ПО ВТ_МинусовыеОстатки.СкладОтправитель = ВТ_СуществующиеСборки.Склад
            И ВТ_МинусовыеОстатки.Номенклатура = ВТ_СуществующиеСборки.Номенклатура

Использование агрегатной функции МАКСИМУМ(Ссылка) решает еще одну критическую проблему — предотвращает размножение строк (декартово произведение), которое неизбежно произойдет, если за указанный период на складе найдется несколько документов сборки по одной и той же позиции.

Скрытые ошибки и рекомендации по оптимизации запроса

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

  1. Логическая ошибка в условии связи с остатками:

    Конструкция в секции связи И ВТ_ДанныеДокумента.ПеремещаемоеКоличество > ТоварыНаСкладахОстатки.КоличествоОстаток работает некорректно. Если товара на складе достаточно, связь не срабатывает, поле остатка обращается в NULL, а функция ЕСТЬNULL(..., 0) превращает его в 0. Запрос ошибочно считает, что на складе ничего нет. Соединять регистры остатков нужно исключительно по измерениям (Номенклатура, Склад), а проверку нехватки остатка выполнять в секции ГДЕ.

  2. Отсутствие параметров виртуальных таблиц:

    Вызов РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаДокаПеремещения, ) без условий рассчитывает остатки по всей базе данных. Необходимо обязательно передавать условия отбора в первый параметр виртуальной таблицы: (Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ ВТ_ДанныеДокумента), Склад = &СкладОтправитель).

  3. Сворачивание строк документа:

    Если в табличной части перемещения одна и та же номенклатура указана двумя строками, без предварительной группировки (СГРУППИРОВАТЬ ПО и СУММА(Количество)) каждая строка будет сравниваться с полным складским остатком отдельно, что исказит итоговую потребность в сборке.

  4. Работа с отсутствующими значениями:

    Использование ЕСТЬNULL(Ссылка, НЕОПРЕДЕЛЕНО) создает в результирующей колонке составной тип. По стандартам разработки 1С рекомендуется оставлять значение NULL либо подставлять строго типизированную пустую ссылку через ЗНАЧЕНИЕ(Документ.СборкаТоваров.ПустаяСсылка).

← На главную