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

SQL Server中循环遍历XML元素按序获取节点值的实现方法

问题说明

需要在SQL Server中遍历传入XML结构内的所有<PersonelsIdsVm>节点,逐个提取PersonelId值,在循环中执行自定义业务逻辑。初始提供的代码缺少总节点数统计、按索引取节点值的核心逻辑,也未做XML文档句柄的释放处理,存在内存泄漏风险。

方案1:适配原有OpenXML写法的实现

完全兼容已写的sp_xml_preparedocument初始化逻辑,补全后可直接运行:

DECLARE @PersonelIds XML
    = '<?xml version="1.0" encoding="utf-8"?>
       <ArrayOfPersonelsIdsVm xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
           <PersonelsIdsVm>
               <PersonelId>c2aadd5d-209d-ec11-8f3c-b42e99ed152b</PersonelId>
           </PersonelsIdsVm>
           <PersonelsIdsVm>
               <PersonelId>0d197668-209d-ec11-8f3c-b42e99ed152b</PersonelId>
           </PersonelsIdsVm>
       </ArrayOfPersonelsIdsVm>';

DECLARE @FromDate DATE = CONVERT(DATE, '2022/05/1');
DECLARE @Days INT = 0;

-- XML句柄初始化
DECLARE @handler INT;
EXEC sys.sp_xml_preparedocument @handler OUT, @PersonelIds;

-- 统计节点总数
DECLARE @TotalCount INT;
SELECT @TotalCount = COUNT(*)
FROM OPENXML(@handler, '/ArrayOfPersonelsIdsVm/PersonelsIdsVm', 2)
WITH (PersonelId UNIQUEIDENTIFIER 'PersonelId');

DECLARE @i INT = 1; -- XPath位置索引从1开始计数
DECLARE @CurrentPersonelId UNIQUEIDENTIFIER;

WHILE @i <= @TotalCount
BEGIN
    -- 按索引提取当前循环对应的PersonelId
    SELECT @CurrentPersonelId = PersonelId
    FROM OPENXML(@handler, '/ArrayOfPersonelsIdsVm/PersonelsIdsVm', 2)
    WITH (
        NodeSeq INT '@mp:id', -- 读取节点内置元属性保证顺序和原XML一致
        PersonelId UNIQUEIDENTIFIER 'PersonelId'
    )
    WHERE NodeSeq = @i;

    -- 此处编写自定义业务逻辑,示例为打印当前提取到的ID
    PRINT @CurrentPersonelId;

    SET @i = @i + 1;
END

-- 必须释放XML文档句柄,否则会持续占用MSXML解析器内存
EXEC sys.sp_xml_removedocument @handler;
方案2:更推荐的原生XQuery写法

SQL Server 2005及以上版本支持原生XQuery方法,无需手动初始化/释放句柄,语法更简洁、执行性能更好:

DECLARE @PersonelIds XML
    = '<?xml version="1.0" encoding="utf-8"?>
       <ArrayOfPersonelsIdsVm xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
           <PersonelsIdsVm>
               <PersonelId>c2aadd5d-209d-ec11-8f3c-b42e99ed152b</PersonelId>
           </PersonelsIdsVm>
           <PersonelsIdsVm>
               <PersonelId>0d197668-209d-ec11-8f3c-b42e99ed152b</PersonelId>
           </PersonelsIdsVm>
       </ArrayOfPersonelsIdsVm>';

DECLARE @FromDate DATE = CONVERT(DATE, '2022/05/1');
DECLARE @Days INT = 0;

-- 按XML原有顺序把所有PersonelId存入临时表,生成行号供循环使用
SELECT 
    ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS RowNum,
    PId.value('(PersonelId/text())[1]', 'UNIQUEIDENTIFIER') AS PersonelId
INTO #PersonelList
FROM @PersonelIds.nodes('/ArrayOfPersonelsIdsVm/PersonelsIdsVm') AS T(PId);

DECLARE @TotalCount INT = @@ROWCOUNT;
DECLARE @i INT = 1;
DECLARE @CurrentPersonelId UNIQUEIDENTIFIER;

WHILE @i <= @TotalCount
BEGIN
    SELECT @CurrentPersonelId = PersonelId
    FROM #PersonelList
    WHERE RowNum = @i;

    -- 此处编写自定义业务逻辑
    PRINT @CurrentPersonelId;

    SET @i = @i + 1;
END

DROP TABLE IF EXISTS #PersonelList;

注意事项

  • 如果业务逻辑不需要严格逐行按顺序执行,优先用集合操作关联XML解析结果集直接处理,性能远高于循环写法
  • 使用OpenXML方案时切记最后释放句柄,SQL Server不会自动回收这部分内存
  • XML相关的索引计数均从1开始,不要按常规编程习惯设初始值为0,会导致漏取第一个节点

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 19:18:45