Слияние таблиц значений запросом

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

Предположим, у нас есть 2 таблицы значений. Колонки:

  • Адресат
  • Email
  • Имя
  • Фамилия
  • Отчество
  • Обращение
  • Пол

Мы хотим объединить их, сопоставив по полям «Адресат» и «Email». Так, чтобы в результирующей таблице не было дублирующихся строк (с одинаковыми парами «Адресат — Email»). Остальные поля объединить с приоритетом второй таблицы.

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

&НаСервере
// Функция создает таблицу значений с колонками нужных типов. В нее можно передать таблицу значений, тогда
// данные из переданной таблицы будут загружены в результирующую таблицу.
Функция ПолучитьТипизированнуюТаблицу(ЗагружаемыеДанные = НеОпределено)
	
	КвалификаторыСтроки = Новый КвалификаторыСтроки(50);
	
	СохраненныеДанные = Новый ТаблицаЗначений;
	СохраненныеДанные.Колонки.Добавить("Адресат", Новый ОписаниеТипов("СправочникСсылка.Клиенты"));
	СохраненныеДанные.Колонки.Добавить("Имя", Новый ОписаниеТипов("Строка", , , , КвалификаторыСтроки));
	СохраненныеДанные.Колонки.Добавить("Фамилия", Новый ОписаниеТипов("Строка", , , , КвалификаторыСтроки));
	СохраненныеДанные.Колонки.Добавить("Отчество", Новый ОписаниеТипов("Строка", , , , КвалификаторыСтроки));
	СохраненныеДанные.Колонки.Добавить("Пол", Новый ОписаниеТипов("ПеречислениеСсылка.ПолФизическогоЛица"));
	СохраненныеДанные.Колонки.Добавить("Email", Новый ОписаниеТипов("Строка", , , , КвалификаторыСтроки));
	СохраненныеДанные.Колонки.Добавить("Обращение", Новый ОписаниеТипов("Строка", , , , КвалификаторыСтроки));
	
	Если ЗагружаемыеДанные <> НеОпределено Тогда
		Для Каждого ТекСтр Из ЗагружаемыеДанные Цикл
			НовСтр = СохраненныеДанные.Добавить();
			ЗаполнитьЗначенияСвойств(НовСтр, ТекСтр);
		КонецЦикла;
	КонецЕсли;
	
	Возврат СохраненныеДанные;
	
КонецФункции

Не суть важно, как мы заполняем таблицу, которую собираемся подгружать. В моем случае она заполнялась запросом, после чего типизировалась. Вот существенные строки:

Результат = Запрос.Выполнить().Выгрузить();			
Таблица = ПолучитьТипизированнуюТаблицу(Результат);
ДополнитьАдресатовТаблицей(Таблица);

Ну а теперь, собственно, процедура, которая сольет 2 таблицы. Первую таблицу она получает внутри себя, а вторую — в качестве параметра. В универсальном случае можете передать обе как параметры.

&НаСервере
Процедура ДополнитьАдресатовТаблицей(Таблица)
	
	// Сохраним текущие данные в таблицу
	СохраненныеДанные = ПолучитьТипизированнуюТаблицу(Объект.Адресаты.Выгрузить());	
	
	Запрос = Новый Запрос;
	
	// Передадим сливаемые таблицы в запрос
	Запрос.УстановитьПараметр("СохраненныеДанные", СохраненныеДанные);
	Запрос.УстановитьПараметр("НовыеДанные", Таблица);
	
	Запрос.Текст = 
		// Сохраним переданные таблицы во временные таблицы
		"ВЫБРАТЬ
		|	СохраненныеДанные.Адресат КАК Адресат,
		|	СохраненныеДанные.Фамилия КАК Фамилия,
		|	СохраненныеДанные.Имя КАК Имя,
		|	СохраненныеДанные.Отчество КАК Отчество,
		|	СохраненныеДанные.Пол КАК Пол,
		|	СохраненныеДанные.Email КАК Email,
		|	СохраненныеДанные.Обращение КАК Обращение
		|ПОМЕСТИТЬ ВТ_СохраненныеДанные
		|ИЗ
		|	&СохраненныеДанные КАК СохраненныеДанные
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	НовыеДанные.Адресат КАК Адресат,
		|	НовыеДанные.Фамилия КАК Фамилия,
		|	НовыеДанные.Имя КАК Имя,
		|	НовыеДанные.Отчество КАК Отчество,
		|	НовыеДанные.Пол КАК Пол,
		|	НовыеДанные.Email КАК Email
		|ПОМЕСТИТЬ ВТ_НовыеДанные
		|ИЗ
		|	&НовыеДанные КАК НовыеДанные
		|;
		|
		// Таблицы будем соединять по полям "Адресат" и "Email". Значит, сначала нам надо их объединить. Ведь в каждой таблице
		// может быть пара "Адресат - Email", отсутствующая в другой таблице.
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ВТ_СохраненныеДанные.Адресат КАК Адресат,
		|	ВТ_СохраненныеДанные.Email КАК Email
		|ПОМЕСТИТЬ ВТ_ОбъединениеБезГруппировки
		|ИЗ
		|	ВТ_СохраненныеДанные КАК ВТ_СохраненныеДанные
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|ВЫБРАТЬ
		|	ВТ_НовыеДанные.Адресат,
		|	ВТ_НовыеДанные.Email
		|ИЗ
		|	ВТ_НовыеДанные КАК ВТ_НовыеДанные
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ВТ_ОбъединениеБезГруппировки.Адресат КАК Адресат,
		|	ВТ_ОбъединениеБезГруппировки.Email КАК Email
		|ПОМЕСТИТЬ ВТ_Объединение
		|ИЗ
		|	ВТ_ОбъединениеБезГруппировки КАК ВТ_ОбъединениеБезГруппировки
		|;
		|
		// Объединим поля "Имя", "Фамилия", "Отчество", "Пол" с приоритетом второй таблицы. То есть если поле заполнено во второй
		// таблице, то используем значение оттуда. Иначе - из первой таблицы.
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ВТ_Объединение.Адресат КАК Адресат,
		|	ВТ_Объединение.Email КАК Email,
		|	ВЫБОР
		|		КОГДА ISNULL(ВТ_НовыеДанные.Имя, """") = """"
		|			ТОГДА ВТ_СохраненныеДанные.Имя
		|		ИНАЧЕ ВТ_НовыеДанные.Имя
		|	КОНЕЦ КАК Имя,
		|	ВЫБОР
		|		КОГДА ISNULL(ВТ_НовыеДанные.Фамилия, """") = """"
		|			ТОГДА ВТ_СохраненныеДанные.Фамилия
		|		ИНАЧЕ ВТ_НовыеДанные.Фамилия
		|	КОНЕЦ КАК Фамилия,
		|	ВЫБОР
		|		КОГДА ISNULL(ВТ_НовыеДанные.Отчество, """") = """"
		|			ТОГДА ВТ_СохраненныеДанные.Отчество
		|		ИНАЧЕ ВТ_НовыеДанные.Отчество
		|	КОНЕЦ КАК Отчество,
		|	ВЫБОР
		|		КОГДА ISNULL(ВТ_НовыеДанные.Пол, ЗНАЧЕНИЕ(Перечисление.ПолФизическогоЛица.ПустаяСсылка)) = ЗНАЧЕНИЕ(Перечисление.ПолФизическогоЛица.ПустаяСсылка)
		|			ТОГДА ВТ_СохраненныеДанные.Пол
		|		ИНАЧЕ ВТ_НовыеДанные.Пол
		|	КОНЕЦ КАК Пол,
		|	ВТ_СохраненныеДанные.Обращение КАК Обращение
		|ИЗ
		|	ВТ_Объединение КАК ВТ_Объединение
		|		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СохраненныеДанные КАК ВТ_СохраненныеДанные
		|		ПО ВТ_Объединение.Адресат = ВТ_СохраненныеДанные.Адресат
		|			И ВТ_Объединение.Email = ВТ_СохраненныеДанные.Email
		|		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_НовыеДанные КАК ВТ_НовыеДанные
		|		ПО ВТ_Объединение.Адресат = ВТ_НовыеДанные.Адресат
		|			И ВТ_Объединение.Email = ВТ_НовыеДанные.Email";
		
	// Загрузим результат объединения в таблицу на форме.
	Объект.Адресаты.Очистить();
	Объект.Адресаты.Загрузить(Запрос.Выполнить().Выгрузить());
	
КонецПроцедуры