SQL XML查找和替换

时间:2016-10-13 23:27:23

标签: sql xml tsql

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

我需要能够为我的<variablename>tminus1</variablename>搜索包含XML的字段 (我没有设计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>

3 个答案:

答案 0 :(得分:3)

一步或两步更换

正如其他人所指出的那样,如果要替换给定为<ValueAsString />的节点的文本值,则会出现问题。最后.modify(N'replace value of...要求/text()作为替换的目标,但是没有......简单的方法适用于给定的情况,但如果在您要更改目标节点的<Parameter>为空,因为它位于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

答案 1 :(得分:2)

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

我使用@ JohnCappelletti的示例代码作为基础编写了以下示例,但SET的{​​{1}}子句不同。 UPDATE XQuery说要获得&#34; / Parameters / Parameter&#34;其中&#34; VariableName&#34;元素名称包含在&#34; @ KeyNode&#34;中找到的字符串。变量,然后抓取&#34; ValueAsString&#34;的文本。儿童元素。这将取代&#34; @ NewValue&#34;变量

我唯一能够开始工作的是替换空元素(即/Parameters/Parameter[//VariableName[1]=sql:variable("@KeyNode")]/ValueAsString/text()元素)。我确定有办法,但我不会干涉这些东西,足以了解细微差别。

<ValueAsString />

请注意:SQL Server中的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; 数据始终编码为UTF-16(Little Endian),与XML / NVARCHAR / NCHAR相同(但是不要使用NTEXT),因此最好将NTEXT用于包含XML的字符串数据,并记住使用大写字母 - NVARCHAR为文字加前缀。

答案 2 :(得分:1)

使用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>