假设我有一个名为tblMap
的映射表,它仅将旧的属性ID映射到新的属性ID(oldID-> newID)。注意:newID不包含在oldID的列表中。
然后,我有一个表tblData
,其中包含一个具有多个属性id
的xml字符串。我想用id
中找到的newID替换所有当前属性tblMap
。如果在tblMap
中找不到ID映射,则应保持原样。关于如何实现此目标的任何提示?
我尝试过的事情:
我正在尝试使用This StackOverflow Article中所述的XMLText.modify('replace value of ...')
来强制执行某些操作,但是并没有成功使其正常工作。
CREATE TABLE tblmap (
oldid INT,
newid INT
)
GO
INSERT INTO tblMap
VALUES
( 58, 1002),
( 85, 5002),
( 70, 3202),
(2, 2340),
(5, 7432)
GO
CREATE TABLE tblData ( [SourceID] int, [SourceRecID] bigint, [Value] xml )
GO
INSERT INTO tblData
VALUES
( 1, 0, N'<attributes><attribute id="58" value="0" /><attribute id="86" value="1" /><attribute id="85" value="1" /><attribute id="70" value="0" /><attribute id="38" value="0" /><attribute id="68" value="0" /><attribute id="42" value="1" /><attribute id="67" value="1" /><attribute id="62" value="1" /></attributes>' ),
( 1, 686, N'<attributes><attribute id="1" value="0.25" /><attribute id="4" value="1" /><attribute id="10" value="3" /><attribute id="11" value="1" /><attribute id="12" value="6" /></attributes>' ),
( 1, 687, N'<attributes><attribute id="1" value="2.00" /><attribute id="2" value="60.00" /><attribute id="3" value="-1" /><attribute id="5" value="252.00" /><attribute id="6" value="0" /><attribute id="7" value="1" /><attribute id="9" value="1" /><attribute id="10" value="1" /><attribute id="11" value="2" /><attribute id="12" value="10" /></attributes>' ),
( 1, 688, N'<attributes><attribute id="1" value="2.00" /><attribute id="2" value="60.00" /><attribute id="3" value="-1" /><attribute id="5" value="252.00" /><attribute id="6" value="0" /><attribute id="7" value="1" /><attribute id="11" value="2" /><attribute id="12" value="10" /></attributes>' )
SELECT *
FROM tblMap
GO
SELECT *
FROM tblData
GO
为方便起见,我在这里构造了所有模式/样本数据: https://rextester.com/MUMI61854
答案 0 :(得分:2)
我将尝试完全重新创建整个XML(或更确切地说是/attributes
节点)并使用新值更新表:
declare @tblmap table (oldid INT, newid INT);
INSERT INTO @tblMap
VALUES
( 58, 1002),
( 85, 5002),
( 70, 3202),
(2, 2340),
(5, 7432);
declare @tblData table ([SourceID] int, [SourceRecID] bigint, [Value] xml);
INSERT INTO @tblData
VALUES
( 1, 0, N'<attributes><attribute id="58" value="0" /><attribute id="86" value="1" /><attribute id="85" value="1" /><attribute id="70" value="0" /><attribute id="38" value="0" /><attribute id="68" value="0" /><attribute id="42" value="1" /><attribute id="67" value="1" /><attribute id="62" value="1" /></attributes>' ),
( 1, 686, N'<attributes><attribute id="1" value="0.25" /><attribute id="4" value="1" /><attribute id="10" value="3" /><attribute id="11" value="1" /><attribute id="12" value="6" /></attributes>' ),
( 1, 687, N'<attributes><attribute id="1" value="2.00" /><attribute id="2" value="60.00" /><attribute id="3" value="-1" /><attribute id="5" value="252.00" /><attribute id="6" value="0" /><attribute id="7" value="1" /><attribute id="9" value="1" /><attribute id="10" value="1" /><attribute id="11" value="2" /><attribute id="12" value="10" /></attributes>' ),
( 1, 688, N'<attributes><attribute id="1" value="2.00" /><attribute id="2" value="60.00" /><attribute id="3" value="-1" /><attribute id="5" value="252.00" /><attribute id="6" value="0" /><attribute id="7" value="1" /><attribute id="11" value="2" /><attribute id="12" value="10" /></attributes>' );
SELECT * FROM @tblMap;
SELECT * FROM @tblData;
-- Update table with new XML
with cte as (
select d.*, (
select isnull(m.newid, a.c.value('./@id', 'int')) as [@id], a.c.value('./@value', 'nvarchar(max)') as [@value]
from d.Value.nodes('/attributes[1]/attribute') a(c)
left join @tblmap m on m.oldid = a.c.value('./@id', 'int')
for xml path('attribute'), type, root('attributes')
) as [NewValue]
from @tblData d
)
update c set Value = NewValue
from cte c;
-- New version
select * from @tblData;
(我已将您的表转换为表变量,因为它在实例上的足迹为零。其他所有内容都是相同的。)
不幸的是,如果您的实际XML模式比示例显示的要复杂,并且在/attributes
节点下包含其他不可预测的元素和/或属性,则这种方法的实施将变得非常困难。在那种情况下,我建议使用FLWOR(速度慢,至少对我来说很难编写)或游标更新。
调试:
-- Update table with new XML
with cte as (
select d.*, (
select isnull(m.newid, a.c.value('./@id', 'int')) as [@id], a.c.value('./@value', 'nvarchar(max)') as [@value]
from d.Value.nodes('/attributes[1]/attribute') a(c)
left join @tblmap m on m.oldid = a.c.value('./@id', 'int')
for xml path('attribute'), type, root('attributes')
) as [NewValue]
from @tblData d
)
SELECT c.SourceID,
c.SourceRecID,
c.Value,
c.NewValue
from cte c;
答案 1 :(得分:2)
我的建议是召集XQuery
进行救援(对于已声明的表变量,txh Roger Wolf,也使用了它们……):
declare @tblmap table (oldid INT, newid INT);
INSERT INTO @tblMap
VALUES
( 58, 1002),
( 85, 5002),
( 70, 3202),
(2, 2340),
(5, 7432);
declare @tblData table ([SourceID] int, [SourceRecID] bigint, [Value] xml);
INSERT INTO @tblData
VALUES
( 1, 0, N'<attributes><attribute id="58" value="0" /><attribute id="86" value="1" /><attribute id="85" value="1" /><attribute id="70" value="0" /><attribute id="38" value="0" /><attribute id="68" value="0" /><attribute id="42" value="1" /><attribute id="67" value="1" /><attribute id="62" value="1" /></attributes>' ),
( 1, 686, N'<attributes><attribute id="1" value="0.25" /><attribute id="4" value="1" /><attribute id="10" value="3" /><attribute id="11" value="1" /><attribute id="12" value="6" /></attributes>' ),
( 1, 687, N'<attributes><attribute id="1" value="2.00" /><attribute id="2" value="60.00" /><attribute id="3" value="-1" /><attribute id="5" value="252.00" /><attribute id="6" value="0" /><attribute id="7" value="1" /><attribute id="9" value="1" /><attribute id="10" value="1" /><attribute id="11" value="2" /><attribute id="12" value="10" /></attributes>' ),
( 1, 688, N'<attributes><attribute id="1" value="2.00" /><attribute id="2" value="60.00" /><attribute id="3" value="-1" /><attribute id="5" value="252.00" /><attribute id="6" value="0" /><attribute id="7" value="1" /><attribute id="11" value="2" /><attribute id="12" value="10" /></attributes>' );
-查询将一次完成整个过程
WITH CombineThem AS
(
SELECT d.SourceID
,d.SourceRecID
,d.[Value]
,(SELECT
(SELECT *
FROM @tblMap
FOR XML PATH('map'),ROOT('maps'),TYPE)
,[Value] AS [*]
FOR XML PATH('Combined'),TYPE) AS Combined
FROM @tblData d
)
,updateableCTE AS
(
SELECT ct.[Value]
,ct.Combined
.query('<attributes>
{
for $attr in /Combined/attributes/attribute
return <attribute id="{
(
/Combined/maps/map[oldid[1]=$attr/@id]/newid
,$attr/@id
)[1]
}"
value="{$attr/@value}"/>
}
</attributes>') NewValue
FROM CombineThem ct
)
UPDATE updateableCTE SET [Value]=NewValue;
-检查结果
SELECT * FROM @tblData;
为了使用XQuery
中的映射和数据,我在第一个CTE中创建了一个组合XML。这将包括完整的<attributes>
元素和一个<maps>
元素。
.query()
将遍历属性,并在<maps>
中搜索合适的重新映射。魔术发生在(val1,val2)[1]
中。行为类似于COALESCE()
。它将选择第一个 non-null 值,它是合适的新id或现有值。
最后的步骤不是一次使用.modify()
更新XML,而是一步一步将[Value]
列设置为新创建的XML。
答案 2 :(得分:1)
老实说,这里的ORDER BY (SELECT NULL)
可靠性不是100%,但是,除了希望顺序是节点顺序以外,我没有其他选择。
无论如何,该解决方案涉及动态SQL。可能会有一种“更好”的方式来执行此操作,但是如果有,我不知道。我建议先进行一些体面的测试,但是,这似乎可以得到您想要的结果:
DECLARE @SQL nvarchar(MAX);
SET @SQL = STUFF((SELECT NCHAR(10) +
N'UPDATE tblData' + NCHAR(10) +
N'SET [Value].modify(''replace value of (/attributes/attribute/@id)[' + CONVERT(varchar(4),ROW_NUMBER() OVER (PARTITION BY D.SourceID, D.SourceRecID ORDER BY (SELECT NULL))) + N'] with "' + CONVERT(varchar(4),ISNULL(M.newid,V.AA.value('@id','int'))) + N'"'')' + NCHAR(10) +
N'WHERE SourceID = ' + CONVERT(varchar(4),D.SourceID) + NCHAR(10) +
N' AND SourceRecID = ' + CONVERT(varchar(4),D.SourceRecID) + N';'
FROM tblData D
CROSS APPLY D.[Value].nodes('attributes/attribute') V(AA)
LEFT JOIN tblmap M ON V.AA.value('@id','int') = M.oldid
FOR XML PATH(N'')),1,1,N'');
EXEC sp_executesql @SQL;