2012-11-26 45 views
0

我有一些數據存儲在表中的SQL Server XML數據列中。它用於配置HTML內容(將作爲電子郵件發送)並且正常工作。不過,我現在想改變一些數據,但我似乎無法找到正確的咒語來影響我想要的位。如何修改SQL XML列中的節點內容?

以下列爲例。

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

幾乎:)從編輯您的示例更新第一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,他們非常有幫助。

+0

感謝您的提示。 XPath過濾器。 –