{"id":6296,"date":"2013-11-12T17:00:21","date_gmt":"2013-11-12T17:00:21","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/generate-a-merge-statement-from-table-structure\/"},"modified":"2016-07-28T10:54:15","modified_gmt":"2016-07-28T10:54:15","slug":"generate-a-merge-statement-from-table-structure","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/blogs\/generate-a-merge-statement-from-table-structure\/","title":{"rendered":"Generate a merge statement from table structure"},"content":{"rendered":"<p class=\"MsoNormal\">\/*<\/p>\n<p class=\"MsoNormal\">This code generates a merge statement joining on the natural key<\/p>\n<p class=\"MsoNormal\">and checking all other columns to see if they have changed.<\/p>\n<p class=\"MsoNormal\">The full version deals with type 2 processing and an audit trail but this version is useful.<\/p>\n<p class=\"MsoNormal\">Just the insert or update part is handy too.<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">Change the table at the top (spt_values in master in the version) and the join columns for the merge in @nk.<\/p>\n<p class=\"MsoNormal\">The output generated is at the top and the code to run to generate it below.<\/p>\n<p class=\"MsoNormal\">*\/<\/p>\n<p class=\"MsoNormal\">&#8212; Output<\/p>\n<p class=\"MsoNormal\">merge spt_values a<\/p>\n<p class=\"MsoNormal\">using spt_values b<\/p>\n<p class=\"MsoNormal\">on a.name = b.name<\/p>\n<p class=\"MsoNormal\">and a.number = b.number<\/p>\n<p class=\"MsoNormal\">and a.type = b.type<\/p>\n<p class=\"MsoNormal\">when matched and (1=0<\/p>\n<p class=\"MsoNormal\">or(a.low = b.low) or(a.low is null and b.low is not null) or(a.low is not null and b.low is null)<\/p>\n<p class=\"MsoNormal\">or(a.high = b.high) or(a.high is null and b.high is not null) or(a.high is not null and b.high is null)<\/p>\n<p class=\"MsoNormal\">or(a.status = b.status) or(a.status is null and b.status is not null) or(a.status is not null and b.status is null)<\/p>\n<p class=\"MsoNormal\">)<\/p>\n<p class=\"MsoNormal\">then update set<\/p>\n<p class=\"MsoNormal\">low = b.low<\/p>\n<p class=\"MsoNormal\">, high = b.high<\/p>\n<p class=\"MsoNormal\">, status = b.status<\/p>\n<p class=\"MsoNormal\">when not matched by target then insert<\/p>\n<p class=\"MsoNormal\">(<\/p>\n<p class=\"MsoNormal\">name<\/p>\n<p class=\"MsoNormal\">, number<\/p>\n<p class=\"MsoNormal\">, type<\/p>\n<p class=\"MsoNormal\">, low<\/p>\n<p class=\"MsoNormal\">, high<\/p>\n<p class=\"MsoNormal\">, status<\/p>\n<p class=\"MsoNormal\">)<\/p>\n<p class=\"MsoNormal\">values<\/p>\n<p class=\"MsoNormal\">(<\/p>\n<p class=\"MsoNormal\">b.name<\/p>\n<p class=\"MsoNormal\">, b.number<\/p>\n<p class=\"MsoNormal\">, b.type<\/p>\n<p class=\"MsoNormal\">, b.low<\/p>\n<p class=\"MsoNormal\">, b.high<\/p>\n<p class=\"MsoNormal\">, b.status<\/p>\n<p class=\"MsoNormal\">);<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">&#8212; Generator<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">set nocount on<\/p>\n<p class=\"MsoNormal\">declare @t varchar(128) = &#8216;spt_values&#8217;<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">declare @i int = 0<\/p>\n<p class=\"MsoNormal\">&#8212; this is the natural key on the table used for the merge statement join<\/p>\n<p class=\"MsoNormal\">declare @nk table (ColName varchar(128))<\/p>\n<p class=\"MsoNormal\">insert @nk select &#8216;Number&#8217;<\/p>\n<p class=\"MsoNormal\">insert @nk select &#8216;Name&#8217;<\/p>\n<p class=\"MsoNormal\">insert @nk select &#8216;Type&#8217;<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">declare @cols table (seq int, nkseq int, type int, colname varchar(128))<\/p>\n<p class=\"MsoNormal\">;with cte as<\/p>\n<p class=\"MsoNormal\">(<\/p>\n<p class=\"MsoNormal\">select ordinal_position,<\/p>\n<p class=\"MsoNormal\">type = case when columnproperty(object_id(@t), COLUMN_NAME,&#8217;IsIdentity&#8217;) = 1 then 3<\/p>\n<p class=\"MsoNormal\">when nk.ColName is not null then 1 else 0 end,<\/p>\n<p class=\"MsoNormal\">COLUMN_NAME<\/p>\n<p class=\"MsoNormal\">from information_schema.columns c<\/p>\n<p class=\"MsoNormal\">left join @nk nk<\/p>\n<p class=\"MsoNormal\">on c.column_name = nk.ColName<\/p>\n<p class=\"MsoNormal\">where table_name = @t<\/p>\n<p class=\"MsoNormal\">)<\/p>\n<p class=\"MsoNormal\">insert @cols(seq, nkseq, type, colname)<\/p>\n<p class=\"MsoNormal\">select ordinal_position, row_number() over (partition by type order by ordinal_position) ,<\/p>\n<p class=\"MsoNormal\">type, COLUMN_NAME<\/p>\n<p class=\"MsoNormal\">from cte<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">Declare @result table (i int, j int, k int, data varchar(500))<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, data)<\/p>\n<p class=\"MsoNormal\">select @i, &#8216;merge &#8216; + @t + &#8216; a&#8217;<\/p>\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, data)<\/p>\n<p class=\"MsoNormal\">select @i, &#8216; using cte b&#8217;<\/p>\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, j, data)<\/p>\n<p class=\"MsoNormal\">select @i, nkseq, &#8216; &#8216; + case when nkseq = 1 then &#8216;on&#8217; else &#8216;and&#8217; end + &#8216; a.&#8217; + ColName + &#8216; = b.&#8217; + ColName<\/p>\n<p class=\"MsoNormal\">from @cols<\/p>\n<p class=\"MsoNormal\">where type = 1<\/p>\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, data)<\/p>\n<p class=\"MsoNormal\">select @i, &#8216; when matched and (1=0&#8217;<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, j, k, data)<\/p>\n<p class=\"MsoNormal\">select @i, seq, 1,<\/p>\n<p class=\"MsoNormal\">&#8216; or (a.&#8217; + ColName + &#8216; b.&#8217; + ColName + &#8216;)&#8217;<\/p>\n<p class=\"MsoNormal\">+ &#8216; or (a.&#8217; + ColName + &#8216; is null and b.&#8217; + ColName + &#8216; is not null)&#8217;<\/p>\n<p class=\"MsoNormal\">+ &#8216; or (a.&#8217; + ColName + &#8216; is not null and b.&#8217; + ColName  + &#8216; is null)&#8217;<\/p>\n<p class=\"MsoNormal\">from @cols<\/p>\n<p class=\"MsoNormal\">where type = 1<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, data)<\/p>\n<p class=\"MsoNormal\">select @i, &#8216; )&#8217;<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, data)<\/p>\n<p class=\"MsoNormal\">select @i, &#8216; then update set&#8217;<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, j, data)<\/p>\n<p class=\"MsoNormal\">select @i, nkseq,<\/p>\n<p class=\"MsoNormal\">&#8216; &#8216; + case when nkseq = 1 then &#8216; &#8216; else &#8216;, &#8216; end<\/p>\n<p class=\"MsoNormal\">+ colname + &#8216; = b.&#8217; + colname<\/p>\n<p class=\"MsoNormal\">from @cols<\/p>\n<p class=\"MsoNormal\">where type = 0<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, data)<\/p>\n<p class=\"MsoNormal\">select @i, &#8216; when not matched by target then insert&#8217;<\/p>\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, data)<\/p>\n<p class=\"MsoNormal\">select @i, &#8216; (&#8216;<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, j, data)<\/p>\n<p class=\"MsoNormal\">select @i, seq, &#8216; &#8216; + case when seq = 1 then &#8216; &#8216; else &#8216;, &#8216; end + colname<\/p>\n<p class=\"MsoNormal\">from @cols<\/p>\n<p class=\"MsoNormal\">where type = 3<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, data)<\/p>\n<p class=\"MsoNormal\">select @i, &#8216; )&#8217;<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, data)<\/p>\n<p class=\"MsoNormal\">select @i, &#8216; values&#8217;<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, data)<\/p>\n<p class=\"MsoNormal\">select @i, &#8216; (&#8216;<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, j, data)<\/p>\n<p class=\"MsoNormal\">select @i, seq, &#8216; &#8216; + case when seq = 1 then &#8216; &#8216; else &#8216;, &#8216; end + &#8216;b.&#8217; + colname<\/p>\n<p class=\"MsoNormal\">from @cols<\/p>\n<p class=\"MsoNormal\">where type = 3<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">select @i = @i + 1<\/p>\n<p class=\"MsoNormal\">insert @result(i, data)<\/p>\n<p class=\"MsoNormal\">select @i, &#8216; );&#8217;<\/p>\n<p class=\"MsoNormal\">\n<p class=\"MsoNormal\">select data from @result order by i,j,k,data<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\/* This code generates a merge statement joining on the natural key and checking all other columns to see if they have changed. The full version deals with type 2 processing and an audit trail but this version is useful. Just the insert or update part is handy too. Change the table at the top&#8230;&hellip;<\/p>\n","protected":false},"author":143519,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[],"coauthors":[],"class_list":["post-6296","post","type-post","status-publish","format-standard","hentry","category-blogs"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/6296","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/143519"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=6296"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/6296\/revisions"}],"predecessor-version":[{"id":42336,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/6296\/revisions\/42336"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=6296"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=6296"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=6296"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=6296"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}