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