Sql server 如何通过连接另一个表从数据中更新具有多个相同xml元素的表中的xml
我有一个包含XML列的表。现在,这个XML列有多个类似于数组的标记。每一个标记都有一个属性(),我想通过连接它从表中更新它 表A如下Sql server 如何通过连接另一个表从数据中更新具有多个相同xml元素的表中的xml,sql-server,xml,tsql,xquery,xml.modify,Sql Server,Xml,Tsql,Xquery,Xml.modify,我有一个包含XML列的表。现在,这个XML列有多个类似于数组的标记。每一个标记都有一个属性(),我想通过连接它从表中更新它 表A如下 Id XML 1 "<Root><Object ObjId = "1" Text = "A"><Object Id = "2" Text = "B"></Root>" 2 "<Ro
Id XML
1 "<Root><Object ObjId = "1" Text = "A"><Object Id = "2" Text = "B"></Root>"
2 "<Root><Object ObjId = "1" Text = "M"><Object Id = "12" Text = "N"></Root>"
<Root>
<Object Id = "1" Text = "A">
<Object Id = "2" Text = "B">
<Object Id = "3" Text = "C">
<Object Id = "4" Text = "D">
<Object Id = "5" Text = "E">
</Root>
我找不到在XML.modify方法中创建动态路径的解决方案。下面是我开始的查询,这可能解释了我正在尝试做什么
UPDATE TblA SET XML.modify(replace value of (/Root/Object) with Table_B.Value)
FROM TABLE_A TblA
CROSS APPLY TblA.XML.nodes('/Root/Object') AS xmlObjs(obj)
INNER JOIN Table_B TblB ON TblB.Table_A_Id = TblA.Id AND TblB.ObjId = xmlObjs.obj.value('@ObjId[1]','varchar(MAX)')
您似乎知道:
.modify()
不允许每次调用进行多个更改。您必须使用光标
或WHILE
循环,以便逐个更新每个事件
因此,我建议采用这种方法:
首先,我们创建模型表来模拟您的问题:
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');
--检查结果
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;
针对这个组合的XML,我们可以使用.query()
运行FLWOR查询
- 此查询将通过以下所有节点运行
- 您端数据的相应
(在XML its
中)被分配给$bVal
- 现在我们检查当前元素的名称是否为
- 如果是这样,我们直接编写属性
和Id
,并添加所有其他属性,而不查看它们Text
- 如果没有,我们就按原样退回$elmt
<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()
的循环方法可能更好…很好的答案,+1来自我这边!嗨,Shnugo,谢谢你的回复。在实际场景中,我在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;
<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>
<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>