Всегда все объединения ТЗ делал циклом с проверкой наличия строки перед добавлением. Это конечно для случаев когда нельзя получить данные сразу в одну ТЗ любым методом.
Занимаясь автоматизацией получения данных с ресурса от поставщика, столкнулся с проблемой. количество строк в каталоге поставщика около 100 000 и получение их возможно только частями по принадлежности к группам.
Просмотрев массу примеров, взял 1 за основу. в моем варианте получилось в 2 раза меньше строк. Работает стабильно, но возникла следующая проблема - ТЗ1 по количеству колонок отличается от ТЗ2. Немного дополнил функцию и циклом 2 ТЗ объединяются без проблем. И снова НО, потребовалось получить весь каталог для обработки, а это в моем случае 23 запроса по группам верхнего уровня. Объединение ТЗ циклом занимает от 30 сек до 210 в зависимости от размера получаемого блока. В совокупности у меня получилось почти 20 минутное ожидание чтобы собрать каталог товаров поставщика в 1 ТЗ. Это очень долго.
Поискав примеры объединения 2-х ТЗ через запрос, собрал свой рабочий вариант. Встречал разные примеры, вот один из них http://zapros-1c-8.ru/9-yazik-zaprosov-1c-8/15-union.
И снова НО: а если ТЗ которые нужно объединять не один конкретный вариант, а их много. Писать под каждый вариант отдельный запрос на объединение? Большинство так и делают.
В моем случае возможных вариантов ТЗ что требуется объединить не 1 и 2, а много больше и по мере развития проекта, над которым работаю, их количество будет только расти, поэтому разработал вариант универсальный.
Объединение ведется по любой указанной колонке.
Уже выложив пример процедур с использованием цикла и запроса, ну и конечно получив первые комментарии, при отладке очередной задачи частично переписал обе процедуры. Сразу оговорюсь, ситуации бывают разные, изначально получил более оптимальный алгоритм через запрос, но потом учитывая комментарий об индексе и добавив сворачивание ТЗ, получил что циклом в 2 раза быстрее чем запросом. скорость обработки зависит от многих факторов: количество строк, колонок, уникальных записей. После модернизации, мне более стал подходить вариант циклом, хотя есть другая задача, где запрос с 15% отрывом работает быстрее, так что использую оба варианта.
Вариант запросом не работает если есть колонки типа строка неграниченной длины, для исправления этого пример процедуры ниже.
Вариант 1. Объединение циклом:
Функция ОбъединитьТЗЦиклом(ТЗ1, ТЗ2, КолонкаПоиска, СворачиватьТЗ = Ложь)
Если НЕ ЗначениеЗаполнено(ТЗ1) Тогда
Возврат ТЗ2;
ИначеЕсли НЕ ЗначениеЗаполнено(ТЗ2) Тогда
Возврат ТЗ1;
КонецЕсли;
Если СворачиватьТЗ Тогда
СписокКолонок = "";
КолКолонок = 0;
Для Каждого КолонкаТЗ Из ТЗ1.Колонки Цикл
КолКолонок = КолКолонок + 1;
СписокКолонок = СписокКолонок + КолонкаТЗ.Имя+?(КолКолонок = ТЗ1.Колонки.Количество(),"",",");
КонецЦикла;
ТЗ1.Свернуть(СписокКолонок);
СписокКолонок = "";
КолКолонок = 0;
Для Каждого КолонкаТЗ Из ТЗ2.Колонки Цикл
КолКолонок = КолКолонок + 1;
СписокКолонок = СписокКолонок + КолонкаТЗ.Имя+?(КолКолонок = ТЗ2.Колонки.Количество(),"",",");
КонецЦикла;
ТЗ2.Свернуть(СписокКолонок);
КонецЕсли;
// таблица источник должна быть меньше, что сократит время обработки
Результат = Новый ТаблицаЗначений;
Если ТЗ1.Количество() > ТЗ2.Количество() Тогда
Результат = ТЗ1.Скопировать();
ТЗДонор = ТЗ2.Скопировать();
Иначе
Результат = ТЗ2.Скопировать();
ТЗДонор = ТЗ1.Скопировать();
КонецЕсли;
// Дополняем результурующую ТЗ колонками, что есть только в ТЗДонор
Для Каждого КолонкаТЗ Из ТЗДонор.Колонки Цикл
Если Результат.Колонки.Найти(КолонкаТЗ.Имя) = Неопределено Тогда
Результат.Колонки.Добавить(КолонкаТЗ.Имя);
КонецЕсли;
КонецЦикла;
Результат.Индексы.Добавить(КолонкаПоиска);
// определение количества, это тоже требует времени - делаем вне цикла
РезультатКолонкиКоличество = Результат.Колонки.Количество();
ТЗДонорКолонкиКоличество = ТЗДонор.Колонки.Количество();
Для Каждого СтрокаТЗДонор из ТЗДонор цикл
СтрокаРезультат = Результат.Найти(СтрокаТЗДонор[КолонкаПоиска],КолонкаПоиска);
Если СтрокаРезультат = Неопределено Тогда
НоваяСтрока = Результат.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЗДонор);
Иначе
Если РезультатКолонкиКоличество <> ТЗДонорКолонкиКоличество Тогда
ЗаполнитьЗначенияСвойств(СтрокаРезультат, СтрокаТЗДонор);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции // ОбъединитьТЗЦиклом()
Вариант 2. Объединение запросом:
Функция ОбъединитьТЗЗапросом(ТЗ1, ТЗ2, КолонкаПоиска, СворачиватьТЗ = Ложь)
Если НЕ ЗначениеЗаполнено(ТЗ1) Тогда
Возврат ТЗ2;
ИначеЕсли НЕ ЗначениеЗаполнено(ТЗ2) Тогда
Возврат ТЗ1;
КонецЕсли;
Если СворачиватьТЗ Тогда
СписокКолонок = "";
КолКолонок = 0;
Для Каждого КолонкаТЗ Из ТЗ1.Колонки Цикл
КолКолонок = КолКолонок + 1;
СписокКолонок = СписокКолонок + КолонкаТЗ.Имя+?(КолКолонок = ТЗ1.Колонки.Количество(),"",",");
КонецЦикла;
ТЗ1.Свернуть(СписокКолонок);
СписокКолонок = "";
КолКолонок = 0;
Для Каждого КолонкаТЗ Из ТЗ2.Колонки Цикл
КолКолонок = КолКолонок + 1;
СписокКолонок = СписокКолонок + КолонкаТЗ.Имя+?(КолКолонок = ТЗ2.Колонки.Количество(),"",",");
КонецЦикла;
ТЗ2.Свернуть(СписокКолонок);
КонецЕсли;
ЗапросТекст = "ВЫБРАТЬ"+Символы.ПС;
КолКолонок = 0;
Для Каждого КолонкаТЗ Из ТЗ1.Колонки Цикл
КолКолонок = КолКолонок + 1;
ЗапросТекст = ЗапросТекст + " ТЗ1."+КолонкаТЗ.Имя+?(КолКолонок = ТЗ1.Колонки.Количество(),"",",")+Символы.ПС;
КонецЦикла;
ЗапросТекст = ЗапросТекст +
"ПОМЕСТИТЬ ТЗ1
|ИЗ
| &ТЗ1 КАК ТЗ1
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ"+Символы.ПС;
КолКолонок = 0;
Для Каждого КолонкаТЗ Из ТЗ2.Колонки Цикл
КолКолонок = КолКолонок + 1;
ЗапросТекст = ЗапросТекст + " ТЗ2."+КолонкаТЗ.Имя+?(КолКолонок = ТЗ2.Колонки.Количество(),"",",")+Символы.ПС;
КонецЦикла;
ЗапросТекст = ЗапросТекст +
"ПОМЕСТИТЬ ТЗ2
|ИЗ
| &ТЗ2 КАК ТЗ2
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ"+Символы.ПС;
// колонки результирующей таблицы
КолКолонок = 0;
Для Каждого КолонкаТЗ Из ТЗ1.Колонки Цикл
КолКолонок = КолКолонок + 1;
Если ТЗ2.Колонки.Найти(КолонкаТЗ.Имя) = Неопределено Тогда
ЗапросТекст = ЗапросТекст + " ТЗ1."+КолонкаТЗ.Имя + ?(КолКолонок = ТЗ1.Колонки.Количество(), "", ","+Символы.ПС);
Иначе
ЗапросТекст = ЗапросТекст + " ЕСТЬNULL(ТЗ1."+КолонкаТЗ.Имя+", ТЗ2."+КолонкаТЗ.Имя+") КАК "+КолонкаТЗ.Имя + ?(КолКолонок = ТЗ1.Колонки.Количество(), "", ","+Символы.ПС);
КонецЕсли;
КонецЦикла;
// уникальные колонки могут быть как ТЗ1 так и в ТЗ2, поэтому разницей количества колонок 2-х ТЗ не определить будем ли добавлять
// поэтому перебираем все колонки ТЗ2 и ищем их в ТЗ1
Для Каждого КолонкаТЗ Из ТЗ2.Колонки Цикл
Если ТЗ1.Колонки.Найти(КолонкаТЗ.Имя) = Неопределено Тогда
ЗапросТекст = ЗапросТекст + "," + Символы.ПС + " ТЗ2."+КолонкаТЗ.Имя;
КонецЕсли;
КонецЦикла;
// ПОЛНОЕ - если нужны все записи по колонке поиска
// ВНУТРЕННЕЕ - если нужны только записи присутствующие в ТЗ1 и ТЗ2 по колонке поиска
ЗапросТекст = ЗапросТекст + Символы.ПС +
"ИЗ
| ТЗ1 КАК ТЗ1
| ПОЛНОЕ СОЕДИНЕНИЕ ТЗ2 КАК ТЗ2
| ПО ТЗ1.КолонкаПоиска = ТЗ2.КолонкаПоиска
|";
ЗапросТекст = СтрЗаменить(ЗапросТекст, "КолонкаПоиска", КолонкаПоиска);
Запрос = Новый Запрос;
Запрос.Текст = ЗапросТекст;
Запрос.УстановитьПараметр("ТЗ1", ТЗ1);
Запрос.УстановитьПараметр("ТЗ2", ТЗ2);
Результат = Запрос.Выполнить().Выгрузить();
Возврат Результат;
КонецФункции // ОбъединитьТЗЗапросом()
Для более наглядного примера смотрите обработку, там выбрав 2 документа можно увидеть результат работы обоих методов. Запрос по ТЗ во втором методе формирется по колонкам ТЗ1 и ТЗ2
И небольшое дополнение к решению задачи по объединению двух таблиц значений. Дело в том что при работе с ТЗ в запросе есть определенные ограничения, например строки неограниченной длины там недопустимы, другими словами при создании колонки ТЗ (если тип СТРОКА) нужно ЧЕТКО указать длину строки. Иначе при выполнении запроса получите ошибку: "Тип не может быть выбран в запросе". Поэтому будте внимательны и четко прописывайте тип колонок, а на случай если: некогда, пофиг, ну или просто невозможно это сделать по какой либо причине, предлагаю такую процедуру для исправления:
взято и переработано немного отсюда: ссылка
Функция ПодготовитьТЗ(ТЗВходящая) Экспорт
РабочаяТаблица = Новый ТаблицаЗначений;
ПерваяСтрока = ТЗВходящая[0];
Для Каждого КолонкаТаблицы из ТЗВходящая.Колонки Цикл
ТипКолонки = ""+ТипЗнч(ПерваяСтрока[КолонкаТаблицы.Имя]);
Если Найти(ВРег(ТипКолонки), "СТРОКА")> 0 Тогда
Тип = Новый ОписаниеТипов(ТипКолонки, , Новый КвалификаторыСтроки(500));
Иначе
Тип = Новый ОписаниеТипов(ТипКолонки);
КонецЕсли;
РабочаяТаблица.Колонки.Добавить(КолонкаТаблицы.Имя, Тип);
КонецЦикла;
Для Каждого СтрокаТаблицы из ТЗВходящая Цикл
СтрокаРабочаяТаблица = РабочаяТаблица.Добавить();
Для Каждого КолонкаТаблицы из ТЗВходящая.Колонки Цикл
СтрокаРабочаяТаблица[КолонкаТаблицы.Имя] = СтрокаТаблицы[КолонкаТаблицы.Имя];
КонецЦикла;
КонецЦикла;
Возврат РабочаяТаблица;
КонецФункции // ПодготовитьТЗ