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