【问题标题】:SQL XML find and replaceSQL XML 查找和替换
【发布时间】:2016-10-13 23:27:23
【问题描述】:

所以我正在处理包含 XML 数据的表中的一个字段,并且逐行 XML 字段中的参数数量会有所不同(变量的名称也会有所不同)。

我需要能够在包含 XML 的字段中搜索我的 <variablename>tminus1</variablename> (我没有设计 XML 结构,我只是必须解决它的人)并替换 <ValueAsString>data here</ValueAsString> 使用由监视表更改的触发器动态生成的新数据。

由于 XML 的设置方式,我花了几天时间试图弄清楚这一点,但我不知所措。任何人都可以帮忙吗?触发器部分很容易找到正确的 XML 位置来替换我遇到的困难。

    <Parameters>
<Parameter><VariableID>(1012,14505)</VariableID><VariableName>ArtworkFormat</VariableName><ListID>(1042,1601)</ListID><ListValue>0</ListValue><ValueAsString /></Parameter>
<Parameter><VariableID>(2226,14505)</VariableID><VariableName>ArtworkProofType</VariableName><ListID>(1045,1601)</ListID><ListValue>0</ListValue><ValueAsString /></Parameter>
<Parameter><VariableID>(2224,14505)</VariableID><VariableName>ArtworkReceivedVia</VariableName><ListID>(1043,1601)</ListID><ListValue>0</ListValue><ValueAsString /></Parameter>
<Parameter><VariableID>(2225,14505)</VariableID><VariableName>ArtworkReturnVia</VariableName><ListID>(1044,1601)</ListID><ListValue>0</ListValue><ValueAsString /></Parameter>
<Parameter><VariableID>(10306,14505)</VariableID><VariableName>tminus1</VariableName><ValueAsString>10/12/2016 4:00 PM</ValueAsString></Parameter>
<Parameter><VariableID>(10308,14505)</VariableID><VariableName>tminus3</VariableName><ValueAsString>10/10/2016 4:00 PM</ValueAsString></Parameter>
<Parameter><VariableID>(10307,14505)</VariableID><VariableName>tminus2</VariableName><ValueAsString>10/11/2016 4:00 PM</ValueAsString></Parameter>
</Parameters>

【问题讨论】:

    标签: sql xml tsql


    【解决方案1】:

    一步或两步替换

    正如其他人指出的那样,如果要替换以&lt;ValueAsString /&gt; 给出的节点的文本值,则会出现问题。 .modify(N'replace value of... 最后要求 /text() 作为替换目标,但没有......简单的方法适用于给定的情况,但如果在 &lt;Parameter&gt; 你想更改目标节点为空,因为它在VariableName="ArtworkReturnVia"

    DECLARE @SearchFor NVARCHAR(100)='tminus1';
    DECLARE @SetValue NVARCHAR(100)='NewData';
    
    UPDATE @tbl SET XmlColumn.modify(N'replace value of (/Parameters/Parameter[VariableName=sql:variable("@SearchFor")]/ValueAsString)[1]/text()[1]
                                       with sql:variable("@SetValue")')
    

    一种直接的方法是在任何情况下都删除此元素并进行插入:

    UPDATE @tbl SET XmlColumn.modify(N'delete (/Parameters/Parameter[VariableName=sql:variable("@SearchFor")]/ValueAsString)[1]')
    UPDATE @tbl SET XmlColumn.modify(N'insert <ValueAsString>{sql:variable("@SetValue")}</ValueAsString> into (/Parameters/Parameter[VariableName=sql:variable("@SearchFor")])[1]')
    

    另一种方法:完整的 FLWOR

    UPDATE @tbl SET XmlColumn=XmlColumn.query
    ('<Parameters>
      {
        for $p in /Parameters/Parameter
        return <Parameter>
        {
        if($p/*/text()=sql:variable("@SearchFor")) then
                for $nd in $p/*
                return
                if(local-name($nd)="ValueAsString") then
                        <ValueAsString>{sql:variable("@SetValue")}</ValueAsString>
                else
                        $nd
        else $p
        }
        </Parameter>
      }
    </Parameters>
    ');
    

    另一个:半FLWOR

    WITH Prms AS
    (
        SELECT ID --needs an ID here
              ,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS NodeOrder
              ,p.query('.') AS prm
        FROM @tbl 
        CROSS APPLY XmlColumn.nodes('/Parameters/Parameter') AS A(p)
    )
    UPDATE tbl SET XmlColumn=
    (
    SELECT CASE WHEN prm.value('(/Parameter/VariableName)[1]','nvarchar(max)')=@SearchFor 
                THEN prm.query('<Parameter>
                                {
                                for $nd in /Parameter/*
                                return
                                    if(local-name($nd)="ValueAsString") then
                                        <ValueAsString>{sql:variable("@SetValue")}</ValueAsString>
                                    else
                                        $nd
                                }
                                </Parameter>
                               ')
                ELSE prm END
    FROM Prms
    WHERE Prms.ID=tbl.ID
    ORDER BY Prms.NodeOrder
    FOR XML PATH(''),ROOT('Parameters')
    )
    FROM @tbl AS tbl
    

    【讨论】:

    • 好多了,再也不用担心数据的结构变化了。
    • @Keith,刚刚看到,full-FLWOR 方法可以简化。循环不需要返回未更改的参数...查看编辑版本
    【解决方案2】:

    您需要查看 XML DML,特别是 replace value of 命令。 XML DML 允许通过.modify() XML 函数对 XML 数据进行内联修改,而无需将其转换为NVARCHAR,然后再转换回XML。您可以将其与以下一项或两项一起使用(取决于您从何处获取替换值):

    我以@JohnCappelletti 的示例代码为基础编写了以下示例,但UPDATESET 子句不同。 /Parameters/Parameter[//VariableName[1]=sql:variable("@KeyNode")]/ValueAsString/text() XQuery 表示要获取“/Parameters/Parameter”元素,其中“VariableName”元素名称包含在“@KeyNode”变量中找到的字符串,然后获取“ValueAsString”子元素的文本。这将被“@NewValue”变量中的值替换。

    我唯一无法工作的是替换一个空元素(即&lt;ValueAsString /&gt; 元素)。我敢肯定有办法,但我只是不插手这些东西,以至于无法了解其中的细微差别。

    DECLARE @KeyNode  NVARCHAR(100) = N'tminus1';
    DECLARE @NewValue NVARCHAR(100) = N'data here';
    
    DECLARE @YourTable TABLE ([ID] INT, [XMLData] XML);
    INSERT INTO @YourTable ([ID], [XMLData]) VALUES 
    (1, N'<Parameters>
        <Parameter><VariableID>(1012,14505)</VariableID><VariableName>ArtworkFormat</VariableName><ListID>(1042,1601)</ListID><ListValue>0</ListValue><ValueAsString /></Parameter>
        <Parameter><VariableID>(2226,14505)</VariableID><VariableName>ArtworkProofType</VariableName><ListID>(1045,1601)</ListID><ListValue>0</ListValue><ValueAsString /></Parameter>
        <Parameter><VariableID>(2224,14505)</VariableID><VariableName>ArtworkReceivedVia</VariableName><ListID>(1043,1601)</ListID><ListValue>0</ListValue><ValueAsString /></Parameter>
        <Parameter><VariableID>(2225,14505)</VariableID><VariableName>ArtworkReturnVia</VariableName><ListID>(1044,1601)</ListID><ListValue>0</ListValue><ValueAsString /></Parameter>
        <Parameter><VariableID>(10306,14505)</VariableID><VariableName>tminus1</VariableName><ValueAsString>10/12/2016 4:00 PM</ValueAsString></Parameter>
        <Parameter><VariableID>(10308,14505)</VariableID><VariableName>tminus3</VariableName><ValueAsString>10/10/2016 4:00 PM</ValueAsString></Parameter>
        <Parameter><VariableID>(10307,14505)</VariableID><VariableName>tminus2</VariableName><ValueAsString>10/11/2016 4:00 PM</ValueAsString></Parameter>
    </Parameters>');
    
    UPDATE tmp
    SET    [XMLData].modify(N'
      replace value of 
      (/Parameters/Parameter[//VariableName[1]=sql:variable("@KeyNode")]/ValueAsString/text())[1]
      with             sql:variable("@NewValue")')
    FROM   @YourTable tmp
    WHERE  tmp.[ID] = 1;
    
    SELECT * FROM @YourTable;
    

    请注意:SQL Server 中的XML 数据始终编码为 UTF-16 (Little Endian),与 NVARCHAR / NCHAR / NTEXT 相同(但不要使用 NTEXT ),因此对于包含 XML 的字符串数据,最好使用 NVARCHAR,并记住在字面量前加上大写字母-N

    【讨论】:

    • 如果你想出了一个例子,请联系我。我想看看应该怎么做。
    • @JohnCappelletti 我已经用一个工作示例更新了我的答案。唯一我无法工作的是替换空的&lt;ValueAsString&gt; 元素。享受:)
    • 我也不知道有什么方法可以替换给定为&lt;element /&gt; 的元素的/text()。 On 可能使用两个步骤。一是删除这个元素,二是用新值插入它,而不是不确定的替换。或使用FLWOR...在我身边+1
    【解决方案3】:

    使用 XML 数据进行字符串操作需要格外小心。

    考虑以下(记住这是我的蜥蜴脑方法)

    Declare @KeyNode  varchar(100)='tminus1'
    Declare @NewValue varchar(100)='data here'
    
    Declare @YourTable table (ID int,XMLData xml)
    Insert Into @YourTable values 
    (1,'<Parameters><Parameter><VariableID>(1012,14505)</VariableID><VariableName>ArtworkFormat</VariableName><ListID>(1042,1601)</ListID><ListValue>0</ListValue><ValueAsString /></Parameter><Parameter><VariableID>(2226,14505)</VariableID><VariableName>ArtworkProofType</VariableName><ListID>(1045,1601)</ListID><ListValue>0</ListValue><ValueAsString /></Parameter><Parameter><VariableID>(2224,14505)</VariableID><VariableName>ArtworkReceivedVia</VariableName><ListID>(1043,1601)</ListID><ListValue>0</ListValue><ValueAsString /></Parameter><Parameter><VariableID>(2225,14505)</VariableID><VariableName>ArtworkReturnVia</VariableName><ListID>(1044,1601)</ListID><ListValue>0</ListValue><ValueAsString /></Parameter><Parameter><VariableID>(10306,14505)</VariableID><VariableName>tminus1</VariableName><ValueAsString>10/12/2016 4:00 PM</ValueAsString></Parameter><Parameter><VariableID>(10308,14505)</VariableID><VariableName>tminus3</VariableName><ValueAsString>10/10/2016 4:00 PM</ValueAsString></Parameter><Parameter><VariableID>(10307,14505)</VariableID><VariableName>tminus2</VariableName><ValueAsString>10/11/2016 4:00 PM</ValueAsString></Parameter></Parameters>')
    
    Update @YourTable 
    Set XMLData = (Select (Select U.VariableID
                                 ,U.VariableName
                                 ,U.ListID
                                 ,U.ListValue
                                 ,ValueAsString = case when U.VariableName=@KeyNode then @NewValue else  U.ValueAsString end
                             From ( Select VariableID   = B.value('VariableID[1]'   ,'varchar(max)') 
                                          ,VariableName = B.value('VariableName[1]' ,'varchar(max)') 
                                          ,ListID       = B.value('ListID[1]'       ,'varchar(max)') 
                                          ,ListValue    = B.value('ListValue[1]'    ,'varchar(max)') 
                                          ,ValueAsString= B.value('ValueAsString[1]','varchar(max)') 
                                     From  XMLData.nodes('/Parameters') AS A (Lvl1)
                                     Cross Apply A.Lvl1.nodes('Parameter')AS B(B)
                                  ) U
                             For XML Path('Parameter'),Type
                         ) For XML Path ('Parameters'),Type
                  )    
     From @YourTable
    
    Select * from @YourTable    
    

    更新 XML 是

    <Parameters>
      <Parameter>
        <VariableID>(1012,14505)</VariableID>
        <VariableName>ArtworkFormat</VariableName>
        <ListID>(1042,1601)</ListID>
        <ListValue>0</ListValue>
        <ValueAsString></ValueAsString>
      </Parameter>
      <Parameter>
        <VariableID>(2226,14505)</VariableID>
        <VariableName>ArtworkProofType</VariableName>
        <ListID>(1045,1601)</ListID>
        <ListValue>0</ListValue>
        <ValueAsString></ValueAsString>
      </Parameter>
      <Parameter>
        <VariableID>(2224,14505)</VariableID>
        <VariableName>ArtworkReceivedVia</VariableName>
        <ListID>(1043,1601)</ListID>
        <ListValue>0</ListValue>
        <ValueAsString></ValueAsString>
      </Parameter>
      <Parameter>
        <VariableID>(2225,14505)</VariableID>
        <VariableName>ArtworkReturnVia</VariableName>
        <ListID>(1044,1601)</ListID>
        <ListValue>0</ListValue>
        <ValueAsString></ValueAsString>
      </Parameter>
      <Parameter>
        <VariableID>(10306,14505)</VariableID>
        <VariableName>tminus1</VariableName>                --<< Key Value
        <ValueAsString>data here</ValueAsString>            --<< Updated Value
      </Parameter>
      <Parameter>
        <VariableID>(10308,14505)</VariableID>
        <VariableName>tminus3</VariableName>
        <ValueAsString>10/10/2016 4:00 PM</ValueAsString>
      </Parameter>
      <Parameter>
        <VariableID>(10307,14505)</VariableID>
        <VariableName>tminus2</VariableName>
        <ValueAsString>10/11/2016 4:00 PM</ValueAsString>
      </Parameter>
    </Parameters>
    

    【讨论】:

    • 我喜欢这个,+1,只有两个想法:这仅在您提前知道所有子元素(今天和将来)的情况下才有效,其次:我已经通过 @ 从 xml 中读取987654323@ 使用nvarchar(max) 来避免不需要的转换...(您肯定知道XML 是unicode...)。我自己做了足够长的时间:-) 我用FLWOR 发布了一个答案。我喜欢这种非常奇特的“语言”:-)
    猜你喜欢
    • 1970-01-01
    • 2011-05-30
    • 2023-03-26
    • 1970-01-01
    • 2016-03-27
    • 2016-10-31
    • 1970-01-01
    • 2011-11-24
    • 1970-01-01
    相关资源
    最近更新 更多