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

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

Внешнее соединение в 1С: что это и как использовать в запросах

Что такое внешнее соединение в 1С: левое, правое и полное соединение таблиц в запросах, синтаксис, примеры кода, типичные ошибки и способы проверки результата.

Внешнее соединение в 1С — это способ объединения данных двух таблиц в запросе, при котором строки одной таблицы попадают в результат, даже если в другой таблице им не нашлось соответствия. В отличие от внутреннего соединения, которое возвращает только совпадающие пары, внешнее соединение сохраняет «несовпавшие» строки и подставляет в пустые поля значение NULL. Это базовый инструмент языка запросов 1С, без которого невозможно построить большинство отчётов: например, список всех товаров, включая те, по которым не было продаж.

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

Типичная задача, где без внешнего соединения не обойтись: вывести всех контрагентов и их заказы, при этом контрагенты без заказов тоже должны быть в списке. Внутреннее соединение «отсечёт» таких контрагентов, и отчёт покажет заниженные данные. Левое внешнее соединение сохранит их, а в полях заказа будет NULL, который легко обработать функцией ЕСТЬNULL.

Как устроено внешнее соединение и чем оно отличается от внутреннего

Внутреннее соединение (ВНУТРЕННЕЕ СОЕДИНЕНИЕ) возвращает только те пары строк, для которых выполняется условие связи. Если у товара нет движений по регистру — строки в результате не будет. Это удобно, когда нужны только факты, но опасно, когда нужен полный список объектов.

Внешнее соединение решает обратную задачу: одна из таблиц объявляется ведущей, и все её строки гарантированно попадают в выборку. Для строк без пары поля второй таблицы заполняются NULL. Именно поэтому внешнее соединение почти всегда сопровождается обработкой NULL — через ЕСТЬNULL, проверку «ЕСТЬ NULL» или значения по умолчанию.

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

Виды внешних соединений: левое, правое и полное

Язык запросов 1С поддерживает три вида внешнего соединения. Выбор зависит от того, какая таблица должна быть сохранена целиком.

Левое соединение (ЛЕВОЕ СОЕДИНЕНИЕ) — самое распространённое. Сохраняются все строки таблицы, указанной слева от ключевого слова. Используется в подавляющем большинстве отчётов: «все товары + их остатки», «все сотрудники + их начисления».

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

Полное соединение (ПОЛНОЕ СОЕДИНЕНИЕ) — сохраняет строки обеих таблиц. NULL появляется с обеих сторон: и где нет пары слева, и где нет пары справа. Используется для сверки данных, например при сравнении остатков в двух учётных системах, когда расхождения возможны в обе стороны.

Сравнение вариантов соединений

Левое соединение

Все строки левой таблицы плюс совпадения справа. Подходит для отчётов вида «справочник + данные». Ограничение: строки правой таблицы без пары слева теряются.

Правое соединение

Все строки правой таблицы плюс совпадения слева. По сути дублирует левое; используется редко, обычно заменяется перестановкой таблиц.

Полное соединение

Все строки обеих таблиц, NULL с обеих сторон. Подходит для сверки двух источников. Требует обработки NULL в обоих наборах полей.

Синтаксис и пример запроса с левым соединением

Классический пример: получить список номенклатуры с остатками, включая товары с нулевым остатком. Справочник номенклатуры — ведущая (левая) таблица, виртуальная таблица остатков — присоединяемая.

ВЫБРАТЬ

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

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

ИЗ

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

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

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

ГДЕ

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

Обратите внимание на три детали. Во-первых, таблицам даны псевдонимы (КАК Товары, КАК Остатки) — без них запрос быстро становится нечитаемым. Во-вторых, функция ЕСТЬNULL заменяет NULL нулём, иначе в отчёте остаток будет пустым и сломает итоги. В-третьих, фильтр по пометке удаления стоит в ГДЕ и относится к левой таблице — это безопасно и не нарушает логику соединения.

Если нужно соединение по нескольким полям, условия перечисляются через И: например, связь по номенклатуре и складу одновременно. Каждое дополнительное условие сужает набор совпадений, поэтому лишнее равенство в ПО — частая причина «потерянных» строк.

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

Когда какое соединение выбирать

Простое правило: сначала определите, какой набор данных обязан присутствовать в результате полностью. Эта таблица и становится левой при левом соединении. Если полными должны быть обе стороны — нужно полное соединение.

Для отчётов по справочникам почти всегда выбирают левое соединение от справочника к регистру. Для анализа «есть ли документы без строк» или «есть ли проводки без документа» используют левое соединение с проверкой на NULL: условие «ПрисоединённаяТаблица.Поле ЕСТЬ NULL» выводит именно несовпавшие строки. Это стандартный приём поиска потерянных данных.

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

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

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

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

Обработка NULL и типичные ошибки

NULL — главный источник ошибок при работе с внешними соединениями. Сравнение NULL с любым значением через обычное равенство не даёт истины: условие «Остаток = 0» не найдёт строки, где остаток отсутствует. Для проверки используйте только «ЕСТЬ NULL» или «НЕ ... ЕСТЬ NULL».

Арифметика с NULL тоже даёт NULL: сумма «Цена * Количество» обнулится для несовпавших строк, если не обернуть поля в ЕСТЬNULL. В итогах отчёта это проявляется как заниженные суммы без видимых причин.

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

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

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

Внешнее соединение само по себе не «медленное», но стоимость запроса сильно зависит от того, соединяетесь ли вы с виртуальной таблицей или с физической таблицей регистра. Соединение с виртуальной таблицей остатков или оборотов обычно эффективнее, потому что платформа получает уже агрегированные данные. Соединение с таблицей движений заставляет СУБД обрабатывать все записи до группировки.

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

Также учитывайте, что полное соединение не всегда одинаково оптимизируется разными СУБД. Если запрос с полным соединением работает медленно, его можно заменить объединением двух левых соединений через ОБЪЕДИНИТЬ — иногда это даёт выигрыш, но требует аккуратной дедупликации.

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

Базовая проверка — контрольный пример на малом наборе данных. Возьмите два-три объекта справочника: один с движениями, один без движений, и убедитесь, что в результате присутствуют оба, причём у объекта без движений поля присоединённой таблицы равны NULL или заменены значением по умолчанию.

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

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

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

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

Объект без движений в результате
Норма

Строка присутствует, числовые поля равны 0 или значению из ЕСТЬNULL

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

Строки нет — соединение работает как внутреннее, проверьте условия в ГДЕ

Количество строк результата
Норма

Не превышает число строк ведущей таблицы после фильтров

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

Строк больше — дубли в присоединяемой таблице, нужна группировка

Сумма по числовому полю
Норма

Совпадает с суммой прямого запроса к регистру

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

Сумма занижена (NULL в расчётах) или завышена (дубли строк)

Практический порядок написания запроса

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

Порядок действий
  1. 1
    Определите, какая таблица должна присутствовать в результате полностью — поставьте её первой (левой).
  2. 2
    Добавьте ЛЕВОЕ СОЕДИНЕНИЕ с псевдонимами обеих таблиц и условием связи в ПО.
  3. 3
    Если соединяетесь с регистром — используйте виртуальную таблицу (Остатки, Обороты) или вложенный запрос с группировкой.
  4. 4
    Оберните числовые и строковые поля присоединяемой таблицы в ЕСТЬNULL.
  5. 5
    Разместите фильтры: по левой таблице — в ГДЕ, по правой — в ПО.
  6. 6
    Проверьте результат на контрольном примере: объект с данными, объект без данных, сверка итогов.
При отладке запроса с соединением удобно временно вывести поле-ключ обеих таблиц в результат. По паре «ключ слева — ключ справа» сразу видно, где связь сработала, а где вместо данных подставлен NULL.
ПЗ
Практическое замечание

Ответы на частые вопросы

Чем левое соединение отличается от правого на практике?

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

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

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

Как вывести только те объекты, по которым нет данных в регистре?

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

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

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

Можно ли соединять больше двух таблиц в одном запросе?

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

Что быстрее: соединение с виртуальной таблицей или с физической таблицей регистра?

Обычно соединение с виртуальной таблицей остатков или оборотов эффективнее, потому что СУБД получает уже агрегированные данные. Соединение с физической таблицей движений заставляет обрабатывать все записи регистра до группировки, что на больших объёмах заметно медленнее.

Итог

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

Внешнее соединение сохраняет все строки ведущей таблицы и подставляет NULL там, где пары не нашлось. В большинстве задач достаточно левого соединения с ЕСТЬNULL для числовых полей. Фильтры по присоединяемой таблице держите в ПО, соединяйтесь с сгруппированными данными и проверяйте результат на контрольном примере и сверкой итогов.