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

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

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

Суть проблемы: ловушка секции ГДЕ

Рассмотрим ситуацию, описанную участником форума. В запросе соединяются две таблицы: остатки (левая) и обороты (правая). Автор ожидает увидеть все остатки, а там, где по ним не было выпусков (оборотов), — пустые значения. Однако в результате остаются только те строки, где выпуск зафиксирован. Выясним причину, проанализировав фрагмент кода:


ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ЗапасыНоменклатураПродукция.Обороты(...) КАК ЗапасыОбороты
ПО ЗапасыОстатки.Заказ = ЗапасыОбороты.Заказ
ГДЕ
    ЗапасыОбороты.Регистратор ССЫЛКА Документ.ВыпускНоменклатуры

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

Решение №1: Перенос условия в секцию ПО

Это самый правильный и рекомендуемый способ с точки зрения производительности и логики SQL. Если нам нужно отфильтровать правую таблицу так, чтобы это не влияло на состав строк левой таблицы, условие должно быть частью критерия соединения. Проанализируем, как изменится структура запроса:


ИЗ
    РегистрНакопления.ЗапасыНоменклатураПродукция.Остатки(...) КАК ЗапасыОстатки
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ЗапасыНоменклатураПродукция.Обороты(...) КАК ЗапасыОбороты
    ПО ЗапасыОстатки.Заказ = ЗапасыОбороты.Заказ
        И ЗапасыОстатки.Номенклатура = ЗапасыОбороты.Номенклатура
        И ЗапасыОбороты.Регистратор ССЫЛКА Документ.ВыпускНоменклатуры

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

Решение №2: Использование функции ЕСТЬNULL в фильтре

Если по каким-то причинам перенос условия в ПО неудобен, можно прибегнуть к проверке на NULL непосредственно в секции ГДЕ. Посмотрим на пример реализации:


ГДЕ
    ЕСТЬNULL(ЗапасыОбороты.Регистратор ССЫЛКА Документ.ВыпускНоменклатуры, ИСТИНА)

Такой подход заставляет систему возвращать ИСТИНА даже в тех случаях, когда запись в правой таблице отсутствует. Однако будьте осторожны: использование функций над полями в секции ГДЕ может привести к тому, что СУБД не сможет использовать индексы (запрос перестанет быть SARGable). Это повлечет за собой полное сканирование таблиц и существенное замедление работы базы данных на больших объемах данных.

Особенности работы с виртуальными таблицами

В платформе 1С:Предприятие при работе с регистрами важно различать фильтры в параметрах виртуальных таблиц и фильтры в секции ГДЕ:

  1. Параметры виртуальной таблицы (в скобках): Отрабатывают максимально быстро, так как фильтрация происходит еще на уровне получения данных из БД. Но здесь нельзя наложить условие на тип регистратора для таблицы .Обороты.
  2. Секция ГДЕ: Отрабатывает уже после того, как все таблицы соединены. Это "тяжелый" фильтр, который часто является причиной ошибок в логике соединений.

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

Практические рекомендации

Для создания надежных и быстрых запросов придерживайтесь следующих правил:

Понимание этих базовых принципов работы механизма LEFT JOIN в SQL и 1С позволит вам избегать трудноудавлимых ошибок в отчетах и обеспечивать высокую производительность ваших конфигураций.

← На главную