表dbo.table_a包含XML列foo。每行的 XML 列中都有一个 XML 树。 辅助表dbo.table_b包含旧值old_val和新值new_val
XML 树看起来像这样
<root>
<Customer>
<Name>ACME</Name>
</Customer>
<Def>
<Enabled>true</Enabled>
<CIds>
<Id>ABC</Id>
<Id>DEF</Id>
</CIds>
</Def>
</root>
请注意,/root/Def/CIds 可以包含零个或多个名为 Id 的子节点。
dbo.table_b 看起来像这样
+---------+---------+
| old_val | new_val |
+---------+---------+
| ABC | 123 |
| DEF | 456 |
+---------+---------+
我需要通过将当前 xml 节点值与 old_val 上的 dbo.table_b 连接并替换 xml 来替换每个 Id 的值节点值与 new_val。 ABC 应替换为 123,DEF 替换为 456
最佳答案
没有简单的方法可以实现这一点,因为即使是现在,2018 年,replace value of
XML 方法仍然不支持多个节点的修改。
最简单的方法是插入+删除策略,如下例所示:
declare @TableA table (
Id int identity(1,1) primary key,
XData xml not null
);
declare @TableB table (
OldValue varchar(50),
NewValue int
);
insert into @TableA (XData)
select N'<root>
<Customer>
<Name>ACME</Name>
</Customer>
<Def>
<Enabled>true</Enabled>
<CIds>
<Id>ABC</Id>
<Id>DEF</Id>
</CIds>
</Def>
</root>';
insert into @TableB (OldValue, NewValue)
values
('ABC', 123),
('DEF', 456);
-- Before
select * from @TableA;
update a set XData.modify('insert sql:column("sq.NewData") after /root[1]/Def[1]/CIds[1]')
from @TableA a
cross apply (
select b.NewValue as [Id]
from @TableB b
where a.XData.exist('/root/Def/CIds/Id[text() = sql:column("b.OldValue")]') = 1
for xml path(''), type, root('CIds')
) sq(NewData);
update a set XData.modify('delete /root[1]/Def[1]/CIds[1]')
from @TableA a;
-- After
select * from @TableA;
它的弱点是它会重建整个 /CIds
节点,因此如果其中有任何其他数据(例如属性),重新创建可能会很麻烦。在这种情况下,使用 FLWOR 更新可能会取得更好的成功,但与其他选项相比,它们往往相当慢。
或者,您可以在循环中运行一系列原子更新。尽管听起来令人不快,但它实际上会起作用,特别是如果您的实际 XML 比提供的示例复杂得多。
关于sql-server - 使用基于连接的当前节点值从辅助表更新 xml 节点值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51885869/