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