0

我有一些数据存储在表的 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'

谢谢。

4

1 回答 1

1

几乎:) Edit 中的示例更新了第一个 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 与过滤器实际上不匹配,并且它将使第二个不受影响,尽管是应该更改的那个。有几种方法可以解决这个问题。就我个人而言,我会让 XPath/XQuery.modify为我完成这项工作。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 过滤器的工作原理,它们非常有帮助。

于 2012-11-26T12:22:57.033 回答