There's simply no advantage of using MERGE in this situation. Why overcomplicate? Stick to the KISS principle, for chrissake.
Anyways, here's the script:
declare
@targetTableName varchar(100) = 'target'
,@targetSchemaName varchar(20) = 'dbo'
,@sourceTableName varchar(100) = 'source'
,@sourceSchemaName varchar(20) = 'dbo2'
,@matchCondition varchar(50) = 't.id = s.id'
,@columns varchar(max)
set @columns = (select ','+quotename(c.name)
from sys.tables t
join sys.columns as c on t.object_id = c.object_id
join sys.schemas s on s.schema_id = t.schema_id
where t.name = @targetTableName and s.name = isnull(@targetSchemaName, s.name)
for xml path(''))
--a column name starts with a comma
declare @sql varchar(max) = '
merge @target t
using @source s on @matchCondition
when not matched then
insert (@columns)
values @sourceColumns'
set @sql =
replace(replace(replace(replace(replace(@sql
, '@matchCondition', @matchCondition)
--replace @columns with column list with the first comma removed
, '@columns', stuff(@columns, 1, 1, ''))
--replace @sourceColumns with column list with the 's.' prefix and comma removed
, '@sourceColumns', stuff(replace(@columns, ',', ',s.'),1,1,''))
, '@target', quotename(@targetSchemaName)+'.'+quotename(@targetTableName))
, '@source', quotename(@sourceSchemaName)+'.'+quotename(@sourceTableName))
print @sql
--exec(@sql)
And we'll get something like this:
merge [dbo].[target] t
using [dbo2].[source] s on t.id = s.id
when not matched then
insert ([column1], [column2], [column3], [column4])
values s.[column1], s.[column2], s.[column3], s.[column4]