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

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

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

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

Частая ошибка заключается в том, что условие отбора (например, фильтр по типу контактной информации Тип = &ТипКИ) записывается в секцию ГДЕ. С точки зрения логики работы СУБД это приводит к следующему результату:

  1. Сначала выполняется соединение всех таблиц. Если связь не найдена, поля из правой таблицы заполняются значениями NULL.
  2. Затем выполняется секция ГДЕ. Она проверяет условие для всех строк.
  3. Если в условии мы требуем, чтобы Тип был равен конкретному значению, то все строки, где этот тип равен NULL (а это именно те строки, которые не нашли пару в правой таблице), просто отбрасываются.

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

Правильное решение: перенос условий в блок ПО

Чтобы сохранить все записи из левой таблицы (даже если для них нет совпадений в правой), необходимо перенести условие фильтрации непосредственно в условие соединения, то есть в блок ПО. В этом случае проверка происходит в момент формирования связи. Если данные не найдены, сохранит запись из левой таблицы и проставит NULL в полях правой, что позволит нам корректно обработать их функцией ЕСТЬNULL.

Рассмотрим пример правильной структуры запроса:


ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
    ДоговорныеОбязательства.Договор КАК Договор,
    ДоговорныеОбязательства.Абонент КАК Абонент
ПОМЕСТИТЬ ВТ_Абоненты
ИЗ
    РегистрНакопления.ДоговорныеОбязательства КАК ДоговорныеОбязательства
;

////////////////////////////////////////////////////////////////////////////////

ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
    ВТ_Абоненты.Абонент КАК Абонент,
    ВТ_Абоненты.Договор КАК Договор,
    ЕСТЬNULL(ФизическиеЛицаКонтактнаяИнформация.НомерТелефона, "нет") КАК НомерТелефона
ИЗ
    ВТ_Абоненты КАК ВТ_Абоненты
ЛЕВОЕ СОЕДИНЕНИЕ 
    Справочник.ФизическиеЛица.КонтактнаяИнформация КАК ФизическиеЛицаКонтактнаяИнформация
ПО 
    (ВТ_Абоненты.Абонент = ФизическиеЛицаКонтактнаяИнформация.Ссылка 
    И ФизическиеЛицаКонтактнаяИнформация.Тип = &ТипКИ)
УПОРЯДОЧИТЬ ПО 
    Абонент

Разбор ключевых моментов

Разберем, что именно изменилось и почему это работает:

Советы по оптимизации

При написании запросов также стоит помнить о производительности:

  1. Избыточность РАЗЛИЧНЫЕ (DISTINCT): Если вы уверены, что структура данных в ВТ_Абоненты и в КонтактнаяИнформация не создает дублей при соединении, старайтесь избегать использования РАЗЛИЧНЫЕ. Это заставляет СУБД тратить дополнительные ресурсы на сортировку и дедупликацию всей выборки.
  2. Индексация: Поля, которые участвуют в блоке ПО, должны быть проиндексированы в конфигурации. Это значительно ускоряет выполнение запроса, особенно если объем данных в регистрах и справочниках исчисляется тысячами или миллионами записей.

Применяя этот подход — перенос условий фильтрации правой таблицы из ГДЕ в ПО — вы навсегда избавитесь от проблемы «исчезающих строк» при работе с левыми соединениями. Это фундаментальное правило языка запросов , которое помогает писать быстрый и предсказуемый код.

← На главную