К статье
Практический материал

Разработка и Конфигурация · 11 мин

Левое соединение в запросе 1С: условия в ГДЕ и ПО

Как работает ЛЕВОЕ СОЕДИНЕНИЕ в языке запросов 1С, куда ставить условие — в ГДЕ или в ПО, типичные ошибки, примеры кода и способы проверки результата.

Левое соединение — один из самых используемых инструментов языка запросов 1С. Оно позволяет получить все записи основной таблицы и подтянуть к ним данные из связанной таблицы, даже если соответствий там нет. Однако именно сочетание левого соединения с условием ГДЕ вызывает больше всего ошибок: неправильно размещённое условие молча превращает левое соединение во внутреннее, и разработчик получает неполный результат, не замечая этого.

Ключевая особенность, которую нужно понимать: условие, записанное в секции ГДЕ, применяется уже к результату соединения. Если в правой таблице не нашлось пары, её поля содержат NULL, и любое сравнение этих полей в ГДЕ отфильтрует такую строку. Поэтому условия по полям правой таблицы почти всегда нужно переносить в секцию ПО самого соединения.

Ниже разобраны механика левого соединения, разница между условиями в ПО и ГДЕ, готовые примеры кода, типичные ошибки и способы проверить, что запрос возвращает именно то, что задумано.

Как работает левое соединение в языке запросов 1С

Конструкция ЛЕВОЕ СОЕДИНЕНИЕ.. ПО <условие связи> объединяет две таблицы так, что в результат попадают все строки левой таблицы. Если для строки нашлось соответствие в правой таблице, поля правой таблицы заполняются её значениями. Если соответствия нет — поля правой таблицы получают значение NULL.

Типовой пример: нужно вывести всех сотрудников справочника и их подразделения из регистра сведений, при этом показать и тех, для кого запись в регистре ещё не заведена. Справочник будет левой таблицей, регистр — правой. Сотрудники без записи в регистре попадут в результат с NULL в полях регистра.

Это частая причина «задвоения» данных в отчётах и расхождения итогов при группировке.

Куда писать условие: в ПО или в ГДЕ

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

Рассмотрим пример. Нужно получить все товары и их остатки только по одному складу. Если условие по складу поместить в ГДЕ, то товары без остатков (у них поле склада равно NULL) будут отброшены — итог будет таким же, как при внутреннем соединении. Если же условие по складу перенести в ПО, то товары без остатков на этом складе останутся в выборке с NULL в полях остатков, что обычно и требуется.

Правило формулируется просто: условия по полям левой таблицы — в ГДЕ (или в ПО, разницы для результата нет), условия по полям правой таблицы — строго в ПО. Исключение — когда вы осознанно хотите получить поведение внутреннего соединения, но тогда корректнее явно написать ВНУТРЕННЕЕ СОЕДИНЕНИЕ.

ПО против ГДЕ

Условие в ПО

Фильтрует строки правой таблицы до соединения. Строки левой таблицы без подходящих соответствий остаются в результате с NULL в полях правой таблицы.

Условие в ГДЕ

Фильтрует итоговый результат. Строки, где поля правой таблицы равны NULL, отбрасываются при любом сравнении этих полей — левое соединение вырождается во внутреннее.

Примеры запросов с левым соединением и условиями

Ниже — корректный шаблон: все условия по правой таблице вынесены в ПО, фильтр по левой таблице оставлен в ГДЕ.

ВЫБРАТЬ

Товары.Ссылка КАК Товар,

ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток

ИЗ

Справочник.Номенклатура КАК Товары

ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки

ПО Товары.Ссылка = Остатки.Номенклатура

И Остатки.Склад = &Склад

ГДЕ

НЕ Товары.ПометкаУдаления

Обратите внимание на функцию ЕСТЬNULL: она заменяет NULL в поле остатка нулём, что упрощает дальнейшие вычисления и вывод. Без неё пришлось бы отдельно обрабатывать NULL в коде или в СКД.

Обратный сценарий — найти строки левой таблицы, для которых соответствия нет вообще. Здесь NULL используется целенаправленно:

ВЫБРАТЬ

Сотрудники.Ссылка КАК Сотрудник

ИЗ

Справочник.Сотрудники КАК Сотрудники

ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КадроваяИсторияСотрудников КАК Кадры

ПО Сотрудники.Ссылка = Кадры.Сотрудник

ГДЕ

Кадры.Сотрудник ЕСТЬ NULL

Такой запрос покажет сотрудников, по которым нет ни одной кадровой записи. Это стандартный приём поиска «осиротевших» данных и контроля полноты учёта.

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

Типичные ошибки и их последствия

Самая распространённая ошибка — перенос условия по правой таблице в ГДЕ «по привычке». Запрос выполняется без ошибок синтаксиса, но теряет часть данных. На тестовой базе, где у всех объектов есть связанные записи, проблема может вообще не проявиться, а в реальной базе отчёт начнёт «недосчитывать» строки.

Вторая ошибка — забыть про размножение строк. Если в правой таблице несколько записей на одну строку левой (например, несколько штрихкодов у товара), итоговая выборка вырастет, а суммы при группировке по левой таблице задвоятся. Решение — соединяться с предварительно сгруппированным вложенным запросом или с виртуальной таблицей, где данные уже свёрнуты.

Третья группа ошибок связана с производительностью: соединение без индексированных полей связи, соединение с полной таблицей регистра вместо виртуальной таблицы остатков, выборка лишних полей. На больших базах такой запрос может выполняться минутами и блокировать работу пользователей.

Типичные ошибки
01
Условие по полю правой таблицы в ГДЕ — строки с NULL теряются, левое соединение превращается во внутреннее
02
Соединение с негруппированной таблицей — строки левой таблицы дублируются, суммы в отчётах завышаются
03
Отсутствие ЕСТЬNULL — NULL попадает в вычисления и итоги, результат сравнений с NULL всегда ложен
04
Соединение с физической таблицей регистра вместо виртуальной — резкое падение скорости на больших объёмах

Как проверить, что запрос возвращает правильные данные

Проверку стоит начинать с контрольных чисел. Посчитайте количество строк в левой таблице отдельным простым запросом и сравните с количеством строк результата: при корректном левом соединении с уникальной правой таблицей они должны совпадать. Если результат меньше — где-то отфильтровались NULL-строки; если больше — есть размножение по связи «один ко многим».

Далее проверьте граничные случаи: заведите тестовый объект левой таблицы без связанных записей и убедитесь, что он присутствует в результате с NULL или значением ЕСТЬNULL. Затем заведите объект с несколькими связанными записями и проверьте, что поведение соответствует ожиданию — либо строки дублируются осознанно, либо данные свёрнуты.

Удобный инструмент — консоль запросов (внешняя обработка или встроенная в режиме предприятия в некоторых конфигурациях). Она позволяет быстро менять условия между ПО и ГДЕ и сразу видеть разницу в результате на реальных данных.

Контрольные точки

Как проверить результат

Количество строк результата vs количество записей левой таблицы
Норма

Совпадает при уникальной связи

Тревожный признак

Меньше — потеряны NULL-строки; больше — размножение

Объект без связанных записей
Норма

Присутствует в результате с NULL или значением ЕСТЬNULL

Тревожный признак

Отсутствует — условие по правой таблице попало в ГДЕ

Суммы после группировки
Норма

Сходятся с контрольным отчётом по регистру

Тревожный признак

Завышены кратно количеству связанных записей

Производительность и ограничения

Левое соединение само по себе не медленное, но его стоимость сильно зависит от того, что стоит справа. Соединение с виртуальной таблицей остатков регистра накопления обычно эффективно, потому что платформа строит её по индексированным итогам. Соединение с полной таблицей движений или с другим запросом без индексов по полям связи может привести к полному перебору.

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

Учтите и ограничение СКД: в настройках отчёта пользователь может добавить отбор по полю правой таблицы, и этот отбор применится к результату — то есть сработает как условие в ГДЕ. Если отчёт обязан показывать строки без соответствий, такие поля стоит исключить из доступных для отбора или заранее обрабатывать через ЕСТЬNULL.

Альтернативы левому соединению

Не всегда левое соединение — лучший инструмент. Если нужно просто проверить наличие соответствия, конструкция «В (ВЫБРАТЬ..)» или условие ЕСТЬ NULL через соединение могут уступать по читаемости вложенному запросу с существованием записи. Для отчётов на СКД иногда проще использовать набор данных «объединение» или характеристики, чем городить сложные соединения.

Если задача — дополнить таблицу значений одним полем из справочника, часто достаточно обращения через точку к реквизиту ссылки («разыменование») вместо явного соединения. Платформа сама построит нужное соединение, а текст запроса останется коротким. Однако при разыменовании через несколько уровней («Поле.Реквизит.Реквизит») количество неявных соединений растёт, и явное левое соединение становится предпочтительнее.

Маршрут решения

Как выбрать действие

Нужны все строки левой таблицы и данные правой при наличииЛЕВОЕ СОЕДИНЕНИЕ, условия по правой таблице — в ПО
Нужны только строки, где соответствие естьВНУТРЕННЕЕ СОЕДИНЕНИЕ — честнее и понятнее
Нужно найти строки без соответствийЛЕВОЕ СОЕДИНЕНИЕ + ГДЕ ПолеПравойТаблицы ЕСТЬ NULL
Нужно одно поле из связанного справочникаРазыменование через точку вместо явного соединения

Подводя итог: левое соединение с условиями — инструмент точный, и его надёжность определяется тем, в какой секции стоит каждое условие. Осознанное размещение условий, контроль NULL и проверка на граничных случаях избавляют от подавляющего большинства проблем.

Итог

Что важно запомнить

Условия по правой таблице при левом соединении пишите в ПО, по левой — в ГДЕ. Используйте ЕСТЬNULL для полей правой таблицы. Проверяйте результат на объектах без связанных записей и контролируйте размножение строк при связи «один ко многим».

Частые вопросы

Почему левое соединение ведёт себя как внутреннее?

Почти всегда причина в том, что условие по полю правой таблицы записано в секции ГДЕ. Для строк без соответствия это поле равно NULL, сравнение с NULL не выполняется, и строка отбрасывается. Перенесите условие в секцию ПО соединения.

Как вывести ноль вместо NULL в полях правой таблицы?

Используйте функцию ЕСТЬNULL(Поле, 0) в списке выборки. Для строковых полей вторым аргументом можно передать пустую строку, для дат — пустую дату. Это упрощает вычисления, сортировку и вывод в отчётах.

Можно ли в ПО писать условия, не связанные с равенством полей?

Да, в секции ПО допустимы произвольные логические выражения, включая сравнения с параметрами, проверки на NULL и составные условия через И/ИЛИ. Именно туда и помещают отборы по складу, организации, периоду для правой таблицы.

Почему после добавления левого соединения задвоились суммы в отчёте?

В правой таблице на одну строку левой приходится несколько записей, и строка левой таблицы дублируется. Сгруппируйте правую таблицу во вложенном запросе до соединения либо используйте виртуальную таблицу с уже свёрнутыми данными.

Что быстрее: левое соединение или разыменование через точку?

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

Как найти документы, у которых нет движений по регистру?

Соедините таблицу документов левым соединением с таблицей регистра по регистратору и добавьте в ГДЕ условие «ПолеРегистра ЕСТЬ NULL». В результат попадут только документы, для которых записей в регистре не найдено.