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

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

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

Как объединить две таблицы в запросе 1С: ОБЪЕДИНИТЬ и ОБЪЕДИНИТЬ ВСЕ, левое и внутреннее соединение, примеры кода, типичные ошибки и проверка результата.

Объединение таблиц — одна из базовых операций языка запросов 1С, но под словом «объединить» скрываются две разные задачи. Первая — сложить строки двух таблиц друг под другом (вертикальное объединение через ОБЪЕДИНИТЬ). Вторая — связать таблицы по общему полю и вывести колонки рядом (горизонтальное соединение через СОЕДИНЕНИЕ). Выбор конструкции зависит от того, что именно нужно получить на выходе.

Например, если требуется собрать в один список документы «Поступление товаров» и «Возврат от покупателя», используется ОБЪЕДИНИТЬ. Если же нужно к списку товаров подтянуть остатки из регистра склада — применяется ЛЕВОЕ СОЕДИНЕНИЕ. Смешение этих подходов — самая частая причина ошибок у начинающих разработчиков.

Ниже разобраны оба сценария с примерами кода, правилами совместимости колонок, отличиями ОБЪЕДИНИТЬ от ОБЪЕДИНИТЬ ВСЕ, а также способы проверить результат и избежать типичных проблем с производительностью и дублями строк.

Чем объединение отличается от соединения

В языке запросов 1С, как и в классическом SQL, существуют два принципиально разных способа собрать данные из нескольких источников. Объединение (UNION) складывает результат вертикально: строки первой таблицы, затем строки второй. Число колонок не меняется, меняется количество строк.

Соединение (JOIN) работает горизонтально: к каждой строке одной таблицы подбирается соответствующая строка другой по условию связи, обычно по ссылке, коду или другому ключевому полю. Растёт число колонок, а количество строк зависит от типа соединения и совпадений ключей.

Сравнение подходов

ОБЪЕДИНИТЬ / ОБЪЕДИНИТЬ ВСЕ

Складывает строки вертикально. Подходит для списков однотипных данных из разных источников. Требует одинакового состава колонок.

ЛЕВОЕ СОЕДИНЕНИЕ

Все строки левой таблицы плюс совпавшие данные правой, иначе NULL. Подходит для подтягивания дополнительных реквизитов.

ВНУТРЕННЕЕ СОЕДИНЕНИЕ

Только строки, где ключ совпал в обеих таблицах. Подходит для поиска пересечений.

ПОЛНОЕ СОЕДИНЕНИЕ

Все строки обеих таблиц, где нет пары — NULL. Подходит для сравнения двух наборов данных.

Объединение строк через ОБЪЕДИНИТЬ и ОБЪЕДИНИТЬ ВСЕ

Конструкция ОБЪЕДИНИТЬ складывает результаты двух и более подзапросов в один набор строк. При этом дублирующиеся строки автоматически исключаются — платформа выполняет дополнительную операцию устранения дублей, что стоит ресурсов на больших выборках.

Если дубли допустимы или заведомо невозможны (например, таблицы физически не пересекаются), используйте ОБЪЕДИНИТЬ ВСЕ. Этот вариант работает быстрее, потому что платформа не тратит время на группировку и сравнение строк.

ВЫБРАТЬ

Поступление.Номенклатура КАК Товар,

Поступление.Количество КАК Количество,

"Поступление" КАК ТипОперации

ИЗ

Документ.ПоступлениеТоваров.Товары КАК Поступление

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ

Возврат.Номенклатура,

Возврат.Количество,

"Возврат"

ИЗ

Документ.ВозвратТоваровОтПокупателя.Товары КАК Возврат

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

Правила совместимости колонок

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

Если во второй таблице нет аналога какого-то поля, подставьте NULL или литерал подходящего типа. Нельзя оставить «дырку» — запрос просто не выполнится с ошибкой несоответствия количества полей.

Соединение таблиц по ключевому полю

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

ВЫБРАТЬ

Товары.Ссылка КАК Номенклатура,

Товары.Артикул КАК Артикул,

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

ИЗ

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

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

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

Функция ЕСТЬNULL заменяет NULL на ноль для позиций без остатков. Без неё в колонке «Остаток» появятся значения NULL, которые некорректно участвуют в арифметике и сравнениях: любое сравнение с NULL возвращает не ИСТИНА и не ЛОЖЬ, а NULL.

Какой тип соединения выбрать

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

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

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

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

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

Объединение через временные таблицы

Когда исходные данные нужно предварительно отфильтровать или сгруппировать, удобнее сначала поместить каждую часть во временную таблицу, а затем объединить уже их. Такой запрос читается проще и часто выполняется быстрее, потому что отборы применяются до объединения, а не после.

ВЫБРАТЬ

Продажи.Номенклатура КАК Товар,

СУММА(Продажи.Количество) КАК Количество

ПОМЕСТИТЬ ВТ_Продажи

ИЗ

РегистрНакопления.Продажи.Обороты(, &КонецПериода, , ) КАК Продажи

СГРУППИРОВАТЬ ПО

Продажи.Номенклатура

;

ВЫБРАТЬ

Закупки.Номенклатура КАК Товар,

СУММА(Закупки.Количество) КАК Количество

ПОМЕСТИТЬ ВТ_Закупки

ИЗ

РегистрНакопления.Закупки.Обороты(, &КонецПериода, , ) КАК Закупки

СГРУППИРОВАТЬ ПО

Закупки.Номенклатура

;

ВЫБРАТЬ

ВТ_Продажи.Товар КАК Товар,

ВТ_Продажи.Количество КАК Продано,

ЕСТЬNULL(ВТ_Закупки.Количество, 0) КАК Закуплено

ИЗ

ВТ_Продажи КАК ВТ_Продажи

ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Закупки КАК ВТ_Закупки

ПО ВТ_Продажи.Товар = ВТ_Закупки.Товар

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

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

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

Вторая группа ошибок связана с объединением: несовпадение числа колонок, разный порядок полей при одинаковых именах (платформа сопоставляет поля по позиции, а не по имени) и неявное приведение типов, из-за которого итоговая колонка получает составной тип и ломает последующую логику.

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

Как проверить результат объединения

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

Для отладки удобно выполнять части запроса по отдельности в консоли запросов, а затем собирать их вместе. Если итоговая выборка «плывёт», почти всегда причина находится на этапе отдельного выполнения одной из частей.

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

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

Число строк при ОБЪЕДИНИТЬ ВСЕ
Норма

Равно сумме строк обеих частей

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

Меньше суммы — где-то потерялись строки или применён отбор

Число строк при левом соединении
Норма

Не больше, чем строк в левой таблице (при связи один-к-одному)

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

Строк больше — ключ связи неуникален, строки размножились

Значения в числовых колонках
Норма

Нет NULL там, где идут расчёты

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

NULL в суммах — не применён ЕСТЬNULL

Типы итоговых колонок
Норма

Колонка имеет один ожидаемый тип

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

Составной тип из-за несовместимых полей в частях объединения

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

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

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

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

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

Чем ОБЪЕДИНИТЬ отличается от ОБЪЕДИНИТЬ ВСЕ?

ОБЪЕДИНИТЬ убирает дублирующиеся строки из результата, ОБЪЕДИНИТЬ ВСЕ сохраняет все строки как есть. Второй вариант быстрее, поэтому его следует использовать всегда, когда дубли невозможны или допустимы.

Можно ли объединить таблицы с разным числом колонок?

Напрямую — нет, количество полей в каждой части должно совпадать. Недостающие поля дополняют значением NULL или литералом подходящего типа, например пустой строкой или нулём.

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

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

Как объединить более двух таблиц?

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

Можно ли упорядочить результат объединения?

Да, конструкция УПОРЯДОЧИТЬ ПО ставится один раз в конце всего запроса и применяется к итоговому набору строк. Ставить сортировку внутрь отдельных частей объединения нельзя.

Что выбрать для сверки двух регистров?

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

Итог

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

ОБЪЕДИНИТЬ складывает строки вертикально, СОЕДИНЕНИЕ связывает таблицы по ключу горизонтально. Предпочитайте ОБЪЕДИНИТЬ ВСЕ, следите за совпадением колонок и типов, применяйте ЕСТЬNULL после левых соединений и проверяйте результат по числу строк и контрольным суммам.