This was my suggestion
- save with attributes
- tolerant with the element's position (as long as this element is unique)
Check it out:
DECLARE @xml XML=
N'<CompanyStatus>
<ProductionServers>
<ProductionServer>
<Patch>0</Patch>
<Status>Green</Status>
<Test_Node a="x" b="y" c="z">Yes</Test_Node>
</ProductionServer>
</ProductionServers>
</CompanyStatus>';
--This will create the <Test_Node> with all its attributes (if there are any) with the new element name <Live_Node>:
DECLARE @NewNode XML=
(
SELECT @xml.query(N'let $nd:=(//*[local-name()="Test_Node"])[1]
return
<Live_Node> {$nd/@*}
{$nd/text()}
</Live_Node>
')
);
--this will first insert the "@NewNode" directly after the original, and will remove the original:
SET @xml.modify(N'insert sql:variable("@NewNode") after (//*[local-name()="Test_Node"])[1]');
SET @xml.modify(N'delete (//*[local-name()="Test_Node"])[1]');
SELECT @xml;
The result
<CompanyStatus>
<ProductionServers>
<ProductionServer>
<Patch>0</Patch>
<Status>Green</Status>
<Live_Node a="x" b="y" c="z">Yes</Live_Node>
</ProductionServer>
</ProductionServers>
</CompanyStatus>
UPDATE: The same with tabular data using an updateable CTE:
DECLARE @xmlTable TABLE (YourXml XML);
INSERT INTO @xmlTable VALUES
(--Test_Node has got attributes
N'<CompanyStatus>
<ProductionServers>
<ProductionServer>
<Patch>0</Patch>
<Status>Green</Status>
<Test_Node a="x" b="y" c="z">Yes</Test_Node>
</ProductionServer>
</ProductionServers>
</CompanyStatus>'
)
,( --different position, no attributes
N'<CompanyStatus>
<ProductionServers>
<Test_Node>Yes</Test_Node>
<ProductionServer>
<Patch>0</Patch>
<Status>Green</Status>
</ProductionServer>
</ProductionServers>
</CompanyStatus>'
)
,( --No test node at all
N'<CompanyStatus>
<ProductionServers>
<ProductionServer>
<Patch>0</Patch>
<Status>Green</Status>
</ProductionServer>
</ProductionServers>
</CompanyStatus>'
);
--the updateable CTE returns the original and the new node. This can be updated in one go:
WITH ReadNode AS
(
SELECT t.YourXml.query(N'let $nd:=(//*[local-name()="Test_Node"])[1]
return
<Live_Node> {$nd/@*}
{$nd/text()}
</Live_Node>
') AS NewNode
,t.YourXml AS Original
FROM @xmlTable AS t
)
UPDATE ReadNode SET Original.modify(N'insert sql:column("NewNode") after (//*[local-name()="Test_Node"])[1]');
UPDATE @xmlTable SET YourXml.modify(N'delete (//*[local-name()="Test_Node"])[1]');
SELECT *
FROM @xmlTable