 <p>Someone who knows XML shredding better than I might be able to address this at the XML shredding level, but here I'll perform a dynamic pivot on your updated query. I'll start by writing FinalValues to a staging table so it can be accessed by the dynamic SQL:</p> <pre><code>DECLARE @FIRST XML = '&lt;DBPerson&gt;&lt;firstname&gt;John&lt;/firstname&gt;&lt;lastname&gt;Bob&lt;/lastname&gt;&lt;/DBPerson&gt;', @Second XML = '&lt;FromUI&gt;&lt;lastname&gt;New Bob&lt;/lastname&gt;&lt;age&gt;39&lt;/age&gt;&lt;/FromUI&gt;'; WITH ALLFields AS ( SELECT x.y.value('local-name(.)','varchar(50)') As Element FROM @Second.nodes('FromUI/*') AS x(y) UNION SELECT x.y.value('local-name(.)','varchar(50)') As Element FROM @FIRST.nodes('DBPerson/*') AS x(y) ), Filtered AS ( SELECT Element , @FIRST.value('(DBPerson/*[local-name()=sql:column("Element")])[1]','varchar(max)') AS F , @Second.value('(FromUI/*[local-name()=sql:column("Element")])[1]','varchar(max)') AS S FROM ALLFields AF ), FinalValues AS ( SELECT Element , CASE WHEN S IS NULL THEN F ELSE S END AS V FROM Filtered ) SELECT [age],[firstname],[lastname] FROM FinalValues PIVOT ( MAX(V) FOR Element IN ([age],[firstname],[lastname]) ) AS p FOR XML PATH('DBPerson'); /* Commenting out the dynamic piece to show the straight pivot above SELECT * INTO FinalValues_Staging FROM FinalValues; GO DECLARE @sql NVARCHAR(MAX) , @col NVARCHAR(MAX); SELECT @col = COALESCE(@col, '') + QUOTENAME(Element) + ',' FROM ( SELECT DISTINCT Element FROM FinalValues_Staging ) AS x; SET @col = LEFT(@col, LEN(@col)-1); SET @sql = N'SELECT &lt;COL&gt; FROM dbo.FinalValues_Staging PIVOT ( MAX(V) FOR Element IN (&lt;COL&gt;) ) AS p FOR XML PATH(''DBPerson'')'; SET @sql = REPLACE(@sql, '&lt;COL&gt;', @col); EXEC sp_executeSQL @sql; PRINT @sql; GO -- DROP TABLE FinalValues_Staging */ </code></pre> <p>Result:</p> <pre><code>&lt;DBPerson&gt; &lt;age&gt;39&lt;/age&gt; &lt;firstname&gt;John&lt;/firstname&gt; &lt;lastname&gt;New Bob&lt;/lastname&gt; &lt;/DBPerson&gt; </code></pre>