如果我在表中有一个列为xml,具有以下节点结构:
<ArrayOfPickListObjectBaseModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<PickListObjectBaseModel>
<MasterObjectId>3964405</MasterObjectId>
<IsSelected>false</IsSelected>
</PickListObjectBaseModel>
<PickListObjectBaseModel>
<MasterObjectId>405716</MasterObjectId>
<IsSelected>false</IsSelected>
</PickListObjectBaseModel>
<PickListObjectBaseModel>
<MasterObjectId>5872525</MasterObjectId>
<IsSelected>false</IsSelected>
</PickListObjectBaseModel>
</ArrayOfPickListObjectBaseModel>
我可以成功检索具有特定MasterObjectId
SELECT p.MyPrimaryKey,
m.value('(MasterObjectId)[1]', 'INT') as MasterObjectID,
p.MasterObjectIDs
FROM PickList p
CROSS APPLY MasterObjectIDs.nodes('//PickListObjectBaseModel') AS t1(m)
WHERE m.value('(MasterObjectId)[1]', 'INT') = 3964405
但如果需要,我如何在所有匹配记录中替换该数字?
因此,在上面的示例中,将3964405
替换为999
这是错误的语法,但我认为它类似于:
DECLARE @oldId INT = 13131180;
DECLARE @newId INT = 99999;
UPDATE PickList
SET MasterObjectIDs.modify('replace value of
(//PickListObjectBaseModel/MasterObjectId/text()=sql:variable("@oldId"))[1] with sql:variable("@newId")');
答案 0 :(得分:2)
以下是基于值替换文本节点的完整解决方案
FROM
部分会加快速度,因为它会减少受影响的行数,否则会对所有行执行操作
DECLARE @oldId INT = 13131180;
DECLARE @newId INT = 99999;
UPDATE PickList
SET MasterObjectIDs.modify('replace value of
(//PickListObjectBaseModel/MasterObjectId[text() = sql:variable("@oldId")]/text())[1]
with sql:variable("@newId")')
FROM PickList p
CROSS APPLY MasterObjectIDs.nodes('//PickListObjectBaseModel') AS t1(m)
WHERE m.value('(MasterObjectId)[1]', 'INT') = @oldId
答案 1 :(得分:1)
很好,你找到了一个解决方案(投了票),只是一些提示:
包含三行的虚拟表:
DECLARE @DummyTable TABLE(ID INT IDENTITY, Comment VARCHAR(100), MasterObjectIDs XML);
INSERT INTO @DummyTable VALUES
('Your example', N'<ArrayOfPickListObjectBaseModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<PickListObjectBaseModel>
<MasterObjectId>3964405</MasterObjectId>
<IsSelected>false</IsSelected>
</PickListObjectBaseModel>
<PickListObjectBaseModel>
<MasterObjectId>405716</MasterObjectId>
<IsSelected>false</IsSelected>
</PickListObjectBaseModel>
<PickListObjectBaseModel>
<MasterObjectId>5872525</MasterObjectId>
<IsSelected>false</IsSelected>
</PickListObjectBaseModel>
</ArrayOfPickListObjectBaseModel>'
)
,('More than one target', N'<ArrayOfPickListObjectBaseModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<PickListObjectBaseModel>
<MasterObjectId>3964405</MasterObjectId>
<IsSelected>false</IsSelected>
</PickListObjectBaseModel>
<PickListObjectBaseModel>
<MasterObjectId>405716</MasterObjectId>
<IsSelected>false</IsSelected>
</PickListObjectBaseModel>
<PickListObjectBaseModel>
<MasterObjectId>3964405</MasterObjectId>
<IsSelected>false</IsSelected>
</PickListObjectBaseModel>
</ArrayOfPickListObjectBaseModel>'
),
('no target', N'<ArrayOfPickListObjectBaseModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<PickListObjectBaseModel>
<MasterObjectId>1111</MasterObjectId>
<IsSelected>false</IsSelected>
</PickListObjectBaseModel>
<PickListObjectBaseModel>
<MasterObjectId>405716</MasterObjectId>
<IsSelected>false</IsSelected>
</PickListObjectBaseModel>
<PickListObjectBaseModel>
<MasterObjectId>5872525</MasterObjectId>
<IsSelected>false</IsSelected>
</PickListObjectBaseModel>
</ArrayOfPickListObjectBaseModel>');
- 变量
DECLARE @oldId INT = 3964405;
DECLARE @newId INT = 99999;
- 我使用xml.exist()
,这比你过滤目标行的方法更快:
UPDATE @DummyTable
SET MasterObjectIDs.modify('replace value of
(//PickListObjectBaseModel/MasterObjectId[text() = sql:variable("@oldId")]/text())[1]
with sql:variable("@newId")')
FROM @DummyTable p
WHERE p.MasterObjectIDs.exist('/ArrayOfPickListObjectBaseModel/PickListObjectBaseModel/MasterObjectId[text()=sql:variable("@oldId")]') = 1;
- 结果
SELECT @@ROWCOUNT; --good the filter hits only 2 rows
SELECT * FROM @DummyTable; --bad, the XML more than one occurances is not edited everywhere...
如果您要查找的ID可能会在一个XML中多次出现,那么您的方法将不适用于所有这些,仅适用于第一个...
您可以在循环中运行语句,直到exist()
返回0
,或者您可能会破坏XML并从头开始重建它,如下所示:
WITH UpdateableCTE AS
(
SELECT p.MasterObjectIds AS Original
,B.NewXML
FROM @DummyTable AS p
CROSS APPLY
(
SELECT
(
SELECT CASE WHEN plo.value(N'(MasterObjectId/text())[1]','int')=@oldId THEN @newId ELSE plo.value(N'(MasterObjectId/text())[1]','int') END AS MasterObjectId
,plo.value(N'(IsSelected/text())[1]','nvarchar(max)') AS IsSelected
FROM MasterObjectIDs.nodes(N'/ArrayOfPickListObjectBaseModel/PickListObjectBaseModel') AS A(plo)
FOR XML PATH(N'PickListObjectBaseMode'),ROOT(N'ArrayOfPickListObjectBaseModel'),TYPE
)
) AS B(NewXML)
WHERE p.MasterObjectIDs.exist('/ArrayOfPickListObjectBaseModel/PickListObjectBaseModel/MasterObjectId[text()=sql:variable("@oldId")]') = 1
)
UPDATE UpdateableCTE SET Original=NewXML;
- 只有2行受影响
SELECT @@ROWCOUNT; --good the filter hits only 2 rows
SELECT * FROM @DummyTable; --all occurances are switched...