При разработке отчетов или обработок на 1С:Предприятие часто возникает необходимость получить данные из основной таблицы и дополнить их информацией из подчиненной или связанной таблицы. Однако при использовании ЛЕВОЕ СОЕДИНЕНИЕ многие разработчики сталкиваются с тем, что записи, для которых нет соответствующих данных в правой таблице, просто исчезают из результата. Рассмотрим, почему это происходит и как правильно строить такие запросы.
Частая ошибка заключается в том, что условие отбора (например, фильтр по типу контактной информации Тип = &ТипКИ) записывается в секцию ГДЕ. С точки зрения логики работы СУБД это приводит к следующему результату:
NULL.ГДЕ. Она проверяет условие для всех строк.Тип был равен конкретному значению, то все строки, где этот тип равен NULL (а это именно те строки, которые не нашли пару в правой таблице), просто отбрасываются.В результате, вместо того чтобы получить «пустое» значение (например, «нет»), мы получаем «обрезанный» список записей, из которого исключены все объекты без контактной информации заданного типа. Фактически, ЛЕВОЕ СОЕДИНЕНИЕ превращается во ВНУТРЕННЕЕ СОЕДИНЕНИЕ.
Чтобы сохранить все записи из левой таблицы (даже если для них нет совпадений в правой), необходимо перенести условие фильтрации непосредственно в условие соединения, то есть в блок ПО. В этом случае проверка происходит в момент формирования связи. Если данные не найдены, 1С сохранит запись из левой таблицы и проставит NULL в полях правой, что позволит нам корректно обработать их функцией ЕСТЬNULL.
Рассмотрим пример правильной структуры запроса:
ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
ДоговорныеОбязательства.Договор КАК Договор,
ДоговорныеОбязательства.Абонент КАК Абонент
ПОМЕСТИТЬ ВТ_Абоненты
ИЗ
РегистрНакопления.ДоговорныеОбязательства КАК ДоговорныеОбязательства
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
ВТ_Абоненты.Абонент КАК Абонент,
ВТ_Абоненты.Договор КАК Договор,
ЕСТЬNULL(ФизическиеЛицаКонтактнаяИнформация.НомерТелефона, "нет") КАК НомерТелефона
ИЗ
ВТ_Абоненты КАК ВТ_Абоненты
ЛЕВОЕ СОЕДИНЕНИЕ
Справочник.ФизическиеЛица.КонтактнаяИнформация КАК ФизическиеЛицаКонтактнаяИнформация
ПО
(ВТ_Абоненты.Абонент = ФизическиеЛицаКонтактнаяИнформация.Ссылка
И ФизическиеЛицаКонтактнаяИнформация.Тип = &ТипКИ)
УПОРЯДОЧИТЬ ПО
Абонент
Разберем, что именно изменилось и почему это работает:
ПО: Мы добавили условие ФизическиеЛицаКонтактнаяИнформация.Тип = &ТипКИ непосредственно в условие связи. Теперь платформа ищет только те записи контактной информации, которые нам нужны, еще на этапе соединения таблиц.ЛЕВОЕ СОЕДИНЕНИЕ, любая запись из ВТ_Абоненты останется в выборке в любом случае. Если телефон нужного типа не найден, поля правой таблицы останутся пустыми, но строка из ВТ_Абоненты не исчезнет.ЕСТЬNULL: Она идеально отрабатывает в такой ситуации. Она видит, что поле НомерТелефона вернуло NULL (так как связи по ТипКИ не было), и заменяет его на строку «нет». Это именно то поведение, которое мы ожидали изначально.При написании запросов также стоит помнить о производительности:
РАЗЛИЧНЫЕ (DISTINCT): Если вы уверены, что структура данных в ВТ_Абоненты и в КонтактнаяИнформация не создает дублей при соединении, старайтесь избегать использования РАЗЛИЧНЫЕ. Это заставляет СУБД тратить дополнительные ресурсы на сортировку и дедупликацию всей выборки.ПО, должны быть проиндексированы в конфигурации. Это значительно ускоряет выполнение запроса, особенно если объем данных в регистрах и справочниках исчисляется тысячами или миллионами записей.Применяя этот подход — перенос условий фильтрации правой таблицы из ГДЕ в ПО — вы навсегда избавитесь от проблемы «исчезающих строк» при работе с левыми соединениями. Это фундаментальное правило языка запросов 1С, которое помогает писать быстрый и предсказуемый код.