T-SQL替换XML节点

时间:2016-09-21 20:41:42

标签: sql-server xml tsql

我想用新节点替换XML节点。我试图使这个动态,所以替换节点名称是一个变量

DECLARE @xmlSource AS XML = '<Root><Transactions><ReplaceMe>This information should be gone</ReplaceMe></Transactions></Root>'
DECLARE @xmlInsert AS XML = '<NewNode>New Information</NewNode>'
DECLARE @NodeName NVARCHAR(500) = 'ReplaceMe'

生成的XML应如下所示:

<Root><Transactions><NewNode>New Information</NewNode></Transactions></Root>

2 个答案:

答案 0 :(得分:3)

没有直接的方法可以用另一个节点替换完整的节点。

但你可以删除它并插入新的:

DECLARE @xmlSource AS XML = '<Root><Transactions><ReplaceMe>This information should be gone</ReplaceMe></Transactions></Root>'
DECLARE @xmlInsert AS XML = '<NewNode>New Information</NewNode>'
DECLARE @NodeName NVARCHAR(500) = 'ReplaceMe'

SET @xmlSource.modify('delete /Root/Transactions/*[local-name(.) eq sql:variable("@NodeName")]');

SELECT @xmlSource; --ReplaceMe is gone...

SET @xmlSource.modify('insert sql:variable("@xmlInsert") into (/Root/Transactions)[1]');

SELECT @xmlSource; 

结果

<Root>
  <Transactions>
    <NewNode>New Information</NewNode>
  </Transactions>
</Root>

更新通用解决方案

根据您的评论我明白,您不了解XML,只需要用另一个节点替换您知道名称的一个节点...

这个解决方案是基于字符串的(无论如何都非常难看)并且有一些缺陷:

  • 如果有多个具有此名称的节点,则只会采用第一个节点
  • 如果节点多次存在且内容相同,则会在两个地方都替换
  • 如果您的xml包含CDATA - 部分,它们将被隐式转换为正确转义的普通XML。没有语义上的损失,但这可能会破坏结构验证......

这应该适用于特殊字符,因为所有转换都是从XML到NVARCHAR并返回。两侧的转义字符应保持不变。

否则必须使用递归方法来获取节点的完整路径并动态构建我的第一个语句。这更干净但更沉重......

DECLARE @xmlSource AS XML = '<Root><Transactions><ReplaceMe>This information should be gone</ReplaceMe></Transactions></Root>'
DECLARE @xmlInsert AS XML = '<NewNode>New Information</NewNode>'
DECLARE @NodeName NVARCHAR(500) = 'ReplaceMe'

SELECT 
CAST( 
REPLACE(CAST(@xmlSource AS NVARCHAR(MAX))
       ,CAST(@xmlSource.query('//*[local-name(.) eq sql:variable("@NodeName")][1]') AS NVARCHAR(MAX))
       ,CAST(@xmlInsert AS NVARCHAR(MAX))
        )
AS XML) 

答案 1 :(得分:0)

要使用XML替换,我们需要在要替换的节点之前(或之后)插入新节点。

首先获取我们要替换的节点数:

DECLARE @iterator int = @numToReplace
WHILE @iterator > 0
BEGIN
    SET @xmlSource.modify('insert attribute ToDelete {"delete"} into ((//*[local-name(.) eq sql:variable("@NodeName")])[sql:variable("@iterator")])[1]');
    SET @iterator = @iterator - 1
END

然后遍历每个节点并标记要删除的节点(这允许我们用具有相同名称的节点替换节点)。

((*query*)[sql:variable("@numToReplace")])[1]

n.b。你需要嵌套目标查询SET @iterator = @numToReplace WHILE @iterator > 0 BEGIN SET @xmlSource.modify('insert sql:variable("@xmlInsert") before ((//*[local-name(.) eq sql:variable("@NodeName")][@ToDelete="true"])[sql:variable("@iterator")])[1]') SET @iterator = @iterator - 1; END ,它不喜欢最后一个节点索引器中的变量

然后在每个旧节点之前插入新节点

SET @xmlSource.modify('delete (//*[local-name(.) eq sql:variable("@NodeName")][@ToDelete="true"])')

然后你可以删除所有旧节点

{{1}}