替换存储在SQL Server数据库列中的XML中的节点名称

时间:2017-07-10 18:10:33

标签: sql sql-server xml replace

我想知道如何替换存储在SQL Server数据库中的xml中的子节点名称

示例XML

<CompanyStatus>
 <ProductionServers>
  <ProductionServer>
    <Patch>0</Patch>
    <Status>Green</Status>
    <Test_Node>Yes</Test_Node>
 </ProductionServers>
  </ProductionServer>
</CompanyStatus>

我如何将其更改为以下内容:

<CompanyStatus>
 <ProductionServers>
  <ProductionServer>
    <Patch>0</Patch>
    <Status>Green</Status>
    <Live_Node>Yes</Live_Node>
 </ProductionServers>
  </ProductionServer>
</CompanyStatus>

基本上唯一的变化是<Test_Node>重命名为<Live_Node>,但价值相同。

有一种简单的方法吗?

我的数据库中有大约1000条记录

5 个答案:

答案 0 :(得分:1)

使用XQuery,例如:

create function SwitchToLiveNode(@doc xml)
returns xml
as
begin
    declare @val varchar(200) = @doc.value('(/CompanyStatus/ProductionServers/ProductionServer/Test_Node)[1]', 'varchar(200)')

    declare @newNode xml = concat('<Live_Node>',@val,'</Live_Node>')

    SET @doc.modify('         
    insert sql:variable("@newNode")      
    as last         
    into (/CompanyStatus/ProductionServers/ProductionServer)[1]         
    ')

    set @doc.modify('delete /CompanyStatus/ProductionServers/ProductionServer/Test_Node')         

    return @doc
end

go

declare @doc xml = '
<CompanyStatus>
 <ProductionServers>
  <ProductionServer>
    <Patch>0</Patch>
    <Status>Green</Status>
    <Test_Node>Yes</Test_Node>
 </ProductionServer>
  </ProductionServers>
</CompanyStatus>'



select dbo.SwitchToLiveNode(@doc)

答案 1 :(得分:1)

这是我的建议

  • 使用属性保存
  • 容忍元素的位置(只要此元素是唯一的)

检查出来:

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>';

- 这将创建<Test_Node>及其所有属性(如果有的话),其中包含新元素名称<Live_Node>

DECLARE @NewNode XML=
    (
     SELECT @xml.query(N'let $nd:=(//*[local-name()="Test_Node"])[1]
                         return
                         <Live_Node> {$nd/@*}
                         {$nd/text()}
                         </Live_Node>
                        ')
    );

- 这将首先在原始文件后直接插入"@NewNode",并删除原始文件:

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;

结果

<CompanyStatus>
  <ProductionServers>
    <ProductionServer>
      <Patch>0</Patch>
      <Status>Green</Status>
      <Live_Node a="x" b="y" c="z">Yes</Live_Node>
    </ProductionServer>
  </ProductionServers>
</CompanyStatus>

更新:与使用可更新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>'
);

- 可更新CTE返回原始节点和新节点。这可以一次更新:

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 

答案 2 :(得分:0)

虽然您的问题要求SQL的解决方案,但我认为最好的解决方案是使用XML库正确解析XML,这在C#中是最简单的。

幸运的是,您可以在SQL Server中将其用作SQL-CLR存储过程:

CREATE PROCEDURE ReplaceTestNode(@xml xml) AS EXTERNAL NAME StoredProcedures.ReplaceTestNode

不幸的是,您无法使用System.Xml重命名XML元素。而是创建一个新的替换节点,将其插入与原始节点相同的位置,移动内容(和任何属性),然后删除原始节点。

public static class StoredProcedures {

    [SqlProcedure]
    public static void ReplaceTestNode(SqlXml data, out SqlXml output) {
        XmlDocument doc = new XmlDocument();

        using( XmlReader rdr = data.CreateReader() ) {
            doc.Load( rdr );
        }

        ReplaceElementName( doc, "Test_Node", "Live_Node" );

        using( XmlReader outRdr = new XmlNodeReader( doc ) ) {
            output = new SqlXml( outRdr );
        }
    }
}

private static void ReplaceElementName(XmlDocument doc, String oldName, String newName) {

    XmlNodeList testNodes = doc.GetElementsByTagName( oldName );
    List<XmlElement> testElements = testNodes
        .Where( n => n.NodeType == XmlNodeType.Element )
        .Cast<XmlElement>()
        .ToList();

    foreach( XmlElement element in testElements ) {

        // 1: Create replacement element and insert in the same location:
        XmlElement replacement = doc.CreateElement( newName );
        element.ParentElement.InsertBefore( replacement, element );

        // 2: Move child nodes over
        foreach(XmlNode child in element.ChildNodes) {
            replacement.AppendChild( child );
        }

        // 3: Move attributes over
        foreach(XmlAttribute attrib in element.Attributes) {
            replacement.Attributes.Append( attrib );
        }

        // 4: Remove
        element.ParentElement.Remove( element );
    }
}

答案 3 :(得分:0)

一个快速选项是通过Replace()

(更正了你的xml)

示例

Update YourTable 
   set XMLCol = replace(cast(XMLCol as nvarchar(max)),'Test_Node>','Live_Node>')

更新的XML

<CompanyStatus>
  <ProductionServers>
    <ProductionServer>
      <Patch>0</Patch>
      <Status>Green</Status>
      <Live_Node>Yes</Live_Node>
    </ProductionServer>
  </ProductionServers>
</CompanyStatus>
  

编辑 - 如果Test_Node具有属性(如戴正确指出的那样)

Update YourTable 
   set XMLCol = replace(replace(cast(XMLCol as varchar(max)),'</Test_Node>','</Live_Node>'),'<Test_Node>','<Live_Node>')

答案 4 :(得分:0)

更新 TABLE_NAME 设置 COLUMN_NAME = cast(REPLACE(cast( COLUMN_NAME 为VARCHAR(max)),'Test_Node','Live_Node' )作为XML)

[注意:使用此脚本放置表名和目标列名]