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