您似乎知道的是:.modify()
每次通话不允许进行一次以上的更改。您必须使用CURSOR
orWHILE
循环才能一个接一个地更新每个事件。
因此,我建议这种方法:
首先,我们创建模型表来模拟您的问题:
DECLARE @tblA TABLE(Id INT, [XML] XML)
INSERT INTO @tblA VALUES
(1,N'<Root>
<Object Id = "1" Text = "A"/>
<Object Id = "2" Text = "B"/>
<Object Id = "5" Text = "E"/>
</Root>')
,(2,N'<Root>
<Object Id = "1" Text = "F"/>
<Object Id = "2" Text = "G"/>
<Object Id = "12" Text = "J"/>
<Object Id = "13" Text = "J"/>
</Root>')
DECLARE @tblB TABLE(Table_A_Id INT,[ObjId] INT,[Value] VARCHAR(10));
INSERT INTO @tblB VALUES
(1,1 ,'Q')
,(1,2 ,'R')
,(2,1 ,'S')
,(2,12,'T');
--查询
WITH cte AS
(
SELECT tA.Id
,tA.[XML]
,(
SELECT A.obj.value('@Id','int') AS [@Id]
,COALESCE(tB.[Value],A.obj.value('@Text','varchar(max)')) AS [@Text]
FROM tA.[XML].nodes('/Root/Object') A(obj)
LEFT JOIN @tblB tB ON tB.Table_A_Id=tA.Id AND tB.[ObjId]=A.obj.value('@Id','int')
FOR XML PATH('Object'),ROOT('Root'),TYPE
) AS NewXml
FROM @tblA tA
)
UPDATE cte SET cte.[Xml]=NewXml;
--查看结果
SELECT * FROM @tblA;
简而言之:
- 我们使用可更新的 CTE将表 A 的数据读入列表
- 我们使用相关的子查询来动态创建修改后的 XML。
- 我们在现有 XML 的基础上编写新的 XML。
更新
下次请尽量避免变色龙问题...您的评论让这变成完全不同的东西...下次请关闭一个问题,如果它按原样回答并开始一个新问题,以防您发现您最初的问题问题并没有真正满足您的需求...
试试这个:
DECLARE @tblA TABLE(Id INT, [XML] XML)
INSERT INTO @tblA VALUES
(1,N'<Root>
<AnotherTag val="abc"/>
<AnotherTag val="DEF"/>
<Object Id = "1" Text = "F" OtherProperty = "123" />
<Object Id = "2" Text = "G" SampleProperty = "Anything" DataProperty="Sample Data" />
<Object Id = "12" Text = "I" OtherProperty = "123"/>
<Object Id = "13" Text = "J" DataProperty = "Sample"/>
</Root>')
,(2,N'<Root>
<Object Id = "1" Text = "F"/>
<Object Id = "2" Text = "G"/>
<Object Id = "12" Text = "I"/>
<Object Id = "13" Text = "J"/>
</Root>')
DECLARE @tblB TABLE(Table_A_Id INT,[ObjId] INT,[Value] VARCHAR(10));
INSERT INTO @tblB VALUES
(1,1 ,'Q')
,(1,2 ,'R')
,(2,1 ,'S')
,(2,12,'T');
WITH cte AS
(
SELECT ta.Id
,ta.[XML]
,Combined.[CombXml].query('<Root>
{
for $elmt in /combined/embedded/Root/*
let $bVal := /combined/b_data[ObjId[1] = $elmt/@Id]/Value/text()
return
if(local-name($elmt) eq "Object") then
<Object Id="{$elmt/@Id}" Text="{if(empty($bVal)) then $elmt/@Text else $bVal}">
{$elmt/@*[local-name() != "Id" and local-name() != "Text"]}
</Object>
else
$elmt
}
</Root>') AS NewXml
FROM @tblA ta
OUTER APPLY(SELECT (SELECT [ObjId],[Value]
FROM @tblB tb
WHERE tb.Table_A_Id=ta.Id
FOR XML PATH('b_data'),TYPE) AS [*]
,ta.[XML] AS [embedded]
FOR XML PATH(''),ROOT('combined'),TYPE) Combined([CombXml])
)
UPDATE cte SET [XML] = NewXml;
SELECT * FROM @tblA;
背后的想法:
为了使用 XQuery-FLWOR,我们需要将辅助数据放入XML。
将APPLY
创建一个这样的 XML:
<combined>
<b_data>
<ObjId>1</ObjId>
<Value>Q</Value>
</b_data>
<b_data>
<ObjId>2</ObjId>
<Value>R</Value>
</b_data>
<embedded>
<Root>
<AnotherTag val="abc" />
<AnotherTag val="DEF" />
<Object Id="1" Text="F" OtherProperty="123" />
<Object Id="2" Text="G" SampleProperty="Anything" DataProperty="Sample Data" />
<Object Id="12" Text="J" OtherProperty="123" />
<Object Id="13" Text="J" DataProperty="Sample" />
</Root>
</embedded>
</combined>
针对这个组合的 XML,我们可以使用.query()
.
- 此查询将遍历下面的所有节点
<Root>
- 您的侧数据的对应
<Value>
(在 XML 中<b_data>
)被分配给$bVal
.
- 现在我们检查当前元素的名称是否为
<Object>
.
- 如果是这样,我们编写属性
Id
并Text
直接添加所有其他属性而不查看它们。
- 如果没有,我们就按原样返回 $elmt 。
之后 Id=1 的结果如下所示:
<Root>
<AnotherTag val="abc" />
<AnotherTag val="DEF" />
<Object Id="1" Text="Q" OtherProperty="123" />
<Object Id="2" Text="R" SampleProperty="Anything" DataProperty="Sample Data" />
<Object Id="12" Text="I" OtherProperty="123" />
<Object Id="13" Text="J" DataProperty="Sample" />
</Root>
您可以看到,“Q”和“R”在 Id 为 1 或 2 的地方发生了变化 - 根据侧面数据。
我必须承认,这变得很复杂......
根据您的真实数据和 XML 的复杂性,使用循环方法.modify()
可能会更好......