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

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

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

Разбор исходного запроса и синтаксические ошибки

Проанализируем пример кода, который часто вызывает вопросы у новичков:


ВЫБРАТЬ
    Валюты.Наименование КАК Валюта,
    КурсыВалют.Период КАК Период,
    КурсыВалют.Курс КАК Курс
ИЗ
    Справочник.Валюты КАК Валюты
        ЛЕВОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
        ПО (Валюты.Ссылка = КурсыВалют.Валюта.Ссылка)

В данном фрагменте кода сразу видны несколько моментов, которые стоит оптимизировать или исправить для корректной работы:

  1. Избыточность ключевых слов. Использование слова ВНЕШНЕЕ в конструкции ЛЕВОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ не является ошибкой, но оно избыточно. В языке запросов 1С левое соединение всегда является внешним. Для чистоты кода рекомендуется писать просто ЛЕВОЕ СОЕДИНЕНИЕ.
  2. Разыменование ссылок (Double Join). Обращение КурсыВалют.Валюта.Ссылка — это классическая ошибка производительности. Поле Валюта в регистре сведений уже само по себе является ссылкой. Добавляя .Ссылка, вы заставляете СУБД выполнять дополнительное неявное соединение с таблицей справочника, чтобы получить значение, которое и так уже есть в исходной таблице. Это замедляет запрос. Правильно писать: Валюты.Ссылка = КурсыВалют.Валюта.

Выясним причину: почему пропадают строки с NULL?

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

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

2. Ошибка фильтрации в секции ГДЕ. Это самая коварная ошибка. Предположим, вы хотите увидеть валюты с курсом больше 0. Если вы напишете:


ГДЕ
    КурсыВалют.Курс > 0

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

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

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


ВЫБРАТЬ
    Валюты.Наименование КАК Валюта,
    ЕСТЬNULL(КурсыВалютСрезПоследних.Курс, 0) КАК Курс
ИЗ
    Справочник.Валюты КАК Валюты
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют.СрезПоследних(&Период, ) КАК КурсыВалютСрезПоследних
        ПО Валюты.Ссылка = КурсыВалютСрезПоследних.Валюта

Рассмотрим подробнее, что мы изменили в этом запросе:

Использование функции ЕСТЬNULL

Чтобы результат запроса был удобен для дальнейшей обработки или вывода в отчет, рекомендуется всегда обрабатывать пустые значения. Функция ЕСТЬNULL(Поле, ЗначениеЗамены) проверяет первое поле, и если там NULL, подставляет указанное значение.

Проанализируем ситуацию: если мы не используем эту функцию, то при попытке сложить курс с каким-либо числом в коде 1С мы можем получить ошибку или некорректный результат. Замена NULL на 0 или на пустую строку (в зависимости от типа данных) — признак хорошего тона в разработке.

Оптимизация производительности (Индексы)

При выполнении левого соединения система сопоставляет записи. Чтобы этот процесс не приводил к зависанию базы при большом количестве валют или записей курса, поле, по которому идет связь, должно быть индексировано. В типовых конфигурациях измерение Валюта в регистре сведений обычно является ведущим или имеет признак Индексировать. Если вы создаете свой регистр, обязательно проверьте наличие индекса по полям, участвующим в связи ПО.

Также разберем по шагам, как ведут себя временные таблицы. В сложных запросах эксперты рекомендуют сначала поместить данные из регистра во временную таблицу, проиндексировать её по валюте, и только потом соединять со справочником. Это гарантирует, что SQL-сервер выберет наиболее эффективный план выполнения запроса.

Итоги

Для корректной работы левого соединения и получения всех данных (включая пустые):

  1. Используйте конструкцию ЛЕВОЕ СОЕДИНЕНИЕ без лишних слов.
  2. Не используйте .Ссылка в условиях связи — сравнивайте ссылочные поля напрямую.
  3. Никогда не ставьте условия на правую (подчиненную) таблицу в секции ГДЕ, если хотите сохранить строки с NULL.
  4. Всегда используйте параметры виртуальных таблиц вместо условий ПО там, где это возможно.
  5. Обрабатывайте результат с помощью ЕСТЬNULL для повышения стабильности кода.

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

← На главную