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