如何修改SQL XML列中的节点内容?

时间:2012-11-26 10:13:43

标签: sql-server xml

我有一些数据存储在表中的SQL Server XML数据列中。它用于配置HTML内容(作为电子邮件发送)并且工作正常。但是,我现在想改变一些数据,但我似乎无法找到正确的咒语来影响我想要的位数。

以下面的例子为例。

declare @s1 varchar(max);
declare @s2 varchar(max);
declare @s3 varchar(max);

-- rough approximation of my data
set @s1 = '<email sender="bob"><html><body><table><tbody><tr><td><b>'
set @s2 = '</b></td></tr><tr><td><b>'
set @s3 = '</b></td></tr></tbody></table></body></html></email>'

declare @t table (id int, data xml)

insert into @t values (1, @s1 + 'Hello World' + @s2 + 'Goodbye cruel world' + @s3)
insert into @t values (2, @s1 + 'Hello World' + @s2 + 'I''m leaving you today' + @s3)
insert into @t values (3, @s1 + 'Hello World' + @s2 + 'Goodbye, goodbye, goodbye' + @s3)

select data from @t

update @t -- change sender to "fred"
set data.modify('replace value of (/email/@sender)[1] with "fred"')

select data from @t

select data,x.nd.value('b[1]','varchar(max)') -- find the "hello world" bits
from @t
cross apply data.nodes('email/html/body/table/tbody/tr/td') as x(nd)
where x.nd.value('b[1]','varchar(max)') = 'Hello World'

我想更改“发件人”数据(我认为我已经解决了)和“Hello World”数据 - 我可以找到,但我不知道如何改变。我不确定“交叉应用”语法是怎么回事,并猜测我可以编写XPath的东西只找到没有“where”子句的“Hello World”节点?

我的表中只有大约9行,所以如果需要我可以编写9个更新命令,这是一次性的数据维护任务。

任何帮助都将不胜感激。

修改

这是完成它的方式吗?

update @t
set data.modify('replace value of (email/html/body/table/tbody/tr/td/b/text())[1] with "bonjour monde"')
from @t
cross apply data.nodes('email/html/body/table/tbody/tr/td') as x(nd)
where x.nd.value('b[1]','varchar(max)') = 'Hello World'

感谢。

1 个答案:

答案 0 :(得分:1)

几乎:)来自编辑的示例更新了第一个 email/html/body/table/tbody/tr/td/b,但WHERE子句查找任何 email/html/body/table/tbody/tr/td。因此,如果您的电子邮件中包含两个tr,而第二个与过滤器匹配,则您仍会更新第一个。例如

 insert into @t values (4, '<email sender="bob"><html><body><table><tbody>
   <tr><td><b>Ciao mondo!</b></td></tr><tr><td><b>Goodbye, goodbye, goodbye</b></td></tr>
   <tr><td><b>Hello World</b></td></tr><tr><td><b>Can''t get rid of me!</b></td></tr>
   </tbody></table></body></html></email>');

上面的xml将无法正确更新:它将更改第一个实际上与过滤器不匹配的tr,并且它将使第二个不受影响,尽管应该已经更改过。有几种方法可以解决这个问题。就个人而言,我会让.modify中的XPath / XQuery为我工作。当XML.exist足够时,我也会批评使用交叉申请:

update @t
set data.modify('replace value of (email/html/body/table/tbody/tr/td/b[.="Hello World"]/text())[1] with "bonjour monde"')
from @t
where data.exist('email/html/body/table/tbody/tr/td/b[.="Hello World"]') = 1;

这将更加正确,但必须重复运行,因为一次只能修改一个XML元素(即,如果两个tr具有Hello World,则修改将仅更新第一个)。

可能所有这些要点都适合您,因为您的第一次更新可能会完成这项工作。但是供将来参考。顺便说一句,了解XPath filters work,他们非常有帮助。