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