You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用T-SQL批量更新XML文档中指定属性的元素值

批量更新XML文档中的多个元素属性

需求:将XML中所有带有Store_ID="13"属性的元素,把该属性值改为99。示例XML如下:

declare @x xml 
select @x = N'
    <Games>
      <Game>
        <Place City="LAS" State="NV" />
        <Place City="ATL" State="GA" />
        <Store Store_ID="12" Price_ID="162" Description="Doom" />
        <Store Store_ID="12" Price_ID="575" Description="Pac-man" />
        <Store Store_ID="13" Price_ID="167" Description="Demons v3" />
        <Store Store_ID="13" Price_ID="123" Description="Whatever" />
      </Game>
    </Games>
'
select @x 

已知可通过XQuery定位目标元素:

select t.c.query('.') 
from @x.nodes('.//*[@Store_ID="13"]') as t(c)

单个节点更新可使用replace value of指定索引:

SET @x.modify('  
  replace value of (.//*[@Store_ID="13"]/@Store_ID)[1]  
  with "99"  
');  
SELECT @x;

但直接结合sql:variable的循环会触发错误:

-- 错误实现
declare @numberOfElements int 
select @numberOfElements = count(*) 
from ( 
    select element = t.c.query('.') 
    from @x.nodes('.//*[@Store_ID="13"]') as t(c)
) x 

declare @i int = 1
while @i <= @numberOfElements begin 
    SET @x.modify('  
      replace value of (.//*[@Store_ID="13"]/@Store_ID)[sql:variable("@i")]  
      with "99"  
    ');  
    set @i = @i + 1 ;  
end 
SELECT @x;

错误信息:

Msg 2337, Level 16, State 1, Line 31
XQuery [modify()]: The target of 'replace' must be at most one node, found 'attribute(Store_ID,xdt:untypedAtomic) *'

以下是两种可行的批量更新方案:

方案一:循环更新首个匹配节点

无需预先统计节点数量,每次更新第一个符合条件的节点,直到无匹配项:

declare @x xml 
select @x = N'
    <Games>
      <Game>
        <Place City="LAS" State="NV" />
        <Place City="ATL" State="GA" />
        <Store Store_ID="12" Price_ID="162" Description="Doom" />
        <Store Store_ID="12" Price_ID="575" Description="Pac-man" />
        <Store Store_ID="13" Price_ID="167" Description="Demons v3" />
        <Store Store_ID="13" Price_ID="123" Description="Whatever" />
      </Game>
    </Games>
'

while @x.exist('.//*[@Store_ID="13"]') = 1
begin
    SET @x.modify('  
      replace value of (.//*[@Store_ID="13"]/@Store_ID)[1]  
      with "99"  
    ');  
end

SELECT @x;

此方法规避了sql:variable导致的节点集解析问题,每次仅处理单个明确节点,不会触发错误。

方案二:重新构造XML(高效批量处理)

通过XQuery直接生成新XML,一次性替换所有目标属性,无需循环:

declare @x xml 
select @x = N'
    <Games>
      <Game>
        <Place City="LAS" State="NV" />
        <Place City="ATL" State="GA" />
        <Store Store_ID="12" Price_ID="162" Description="Doom" />
        <Store Store_ID="12" Price_ID="575" Description="Pac-man" />
        <Store Store_ID="13" Price_ID="167" Description="Demons v3" />
        <Store Store_ID="13" Price_ID="123" Description="Whatever" />
      </Game>
    </Games>
'

set @x = @x.query('
    element Games {
        for $g in /Games/Game
        return element Game {
            for $node in $g/node()
            return
                if ($node[self::Store/@Store_ID="13"]) then
                    element Store {
                        for $attr in $node/@*
                        return
                            if ($attr/local-name()="Store_ID") then
                                attribute Store_ID {"99"}
                            else
                                $attr,
                        $node/node()
                    }
                else
                    $node
        }
    }
')

SELECT @x;

该方法通过递归遍历节点,对符合条件的<Store>元素替换属性,一次性完成所有更新,性能优于循环,适合复杂XML或大量节点场景。

表中XML列的批量更新

若需更新表中多行XML列,可基于上述方案调整:

方法一(游标+循环更新)

-- 假设表为GameStores,XML列为GameData
DECLARE @ID int
DECLARE cur CURSOR FOR SELECT ID FROM GameStores WHERE GameData.exist('.//*[@Store_ID="13"]') = 1

OPEN cur
FETCH NEXT FROM cur INTO @ID

WHILE @@FETCH_STATUS = 0
BEGIN
    WHILE EXISTS(SELECT 1 FROM GameStores WHERE ID=@ID AND GameData.exist('.//*[@Store_ID="13"]')=1)
    BEGIN
        UPDATE GameStores
        SET GameData.modify('
            replace value of (.//*[@Store_ID="13"]/@Store_ID)[1]
            with "99"
        ')
        WHERE ID=@ID
    END
    FETCH NEXT FROM cur INTO @ID
END

CLOSE cur
DEALLOCATE cur

方法二(直接构造XML批量更新)

UPDATE GameStores
SET GameData = GameData.query('
    element Games {
        for $g in /Games/Game
        return element Game {
            for $node in $g/node()
            return
                if ($node[self::Store/@Store_ID="13"]) then
                    element Store {
                        for $attr in $node/@*
                        return
                            if ($attr/local-name()="Store_ID") then
                                attribute Store_ID {"99"}
                            else
                                $attr,
                        $node/node()
                    }
                else
                    $node
        }
    }
')
WHERE GameData.exist('.//*[@Store_ID="13"]') = 1

此方式无需游标,直接利用XQuery生成新XML,更新效率更高。

内容的提问来源于stack exchange,提问作者Rory

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 11:05:26