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