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

在SQL Server中对比两个XML文件差异,解决查询结果重复问题

在SQL Server中准确对比XML文件差异(解决节点值重复问题)

需求

  • 对比SQL Server中存储的两个XML文件(File1为最新版本,File2为旧版本)
  • 需识别新增节点、删除节点,以及节点属性的变化
  • 现有查询因忽略节点层级,导致同名属性匹配错误,出现重复结果,需修正

示例XML文件

<section>
  <oper-ins-procref index-levels="3" moidref="G2020685" proc-uid="G2195890" qualref="+Q100107[11/2022]" title="Mobile Device Data" titleref="T25409" uid="G2292969" uid-ref="R1396412" version="5.0">
      <qualifier>
        <builddates id="Q100107" suppressed="true" title="Vehicles Built From: 11/2022" />
      </qualifier>
    </oper-ins-procref>
    <oper-ins-procref index-levels="3" loc-qual="Vehicles With: Emergency Assistance" moidref="G2128321" proc-uid="G2163427" qualref="+Q107191+Q106763+Q107192" title="Emergency Call System Data" titleref="T27313" uid="G2162708" uid-ref="R1303108" version="2.0">
      <qualifier title="Vehicles With: Emergency Assistance">
        <territory id="Q107191" suppressed="true" title="USA" />
        <feature-mfc category="POSITIVE" id="Q106763" title="Emergency Assistance">
          <mfc code="hksab" type="T" vl="vltf" />
          <mfc code="hksab" type="T" vl="vlts" />
          <!-- 省略重复mfc节点 -->
        </feature-mfc>
        <territory id="Q107192" suppressed="true" title="United States of America" />
      </qualifier>
    </oper-ins-procref>
  </section>
  <section index-levels="1" moidref="G1594458" title="Environment" titleref="T21858" uid="G2323014">
    <link-target moidref="G1594459" option="off" proc-uid="G2145049" qualref="+Q107200" title="Protecting the Environment" titleref="T21859" uid="G2335850" uid-ref="R1428999">
      <qualifier>
        <territory id="Q107200" suppressed="true" title="Brazil" />
      </qualifier>
    </link-target>
    <!-- 省略其他link-target节点 -->
  </section>

现有问题代码

DECLARE @XML1 XML
DECLARE @XML2 XML

SET @XML1 = (SELECT TOP 1 Xml FROM TableA NOLOCK WHERE Id=22)

SET @XML2 = (SELECT TOP 1 Xml FROM TableB NOLOCK WHERE Id=34)

;with XML1 as
(
select T.N.value('local-name(.)', 'nvarchar(100)') as NodeName,
         T.N.value('.', 'nvarchar(100)') as Value
  from @XML1.nodes('//@*') as T(N)
  ),
XML2 as
(
select T.N.value('local-name(.)', 'nvarchar(100)') as NodeName,
         T.N.value('.', 'nvarchar(100)') as Value
  from @XML2.nodes('//@*') as T(N)
  )

select coalesce(XML1.NodeName, XML2.NodeName) as NodeName, 
       XML1.Value as Value1, 
       XML2.Value as Value2
from XML1
  full outer join XML2
    on XML1.NodeName = XML2.NodeName
where coalesce(XML1.Value, '') <> coalesce(XML2.Value, '')

问题说明:仅按属性名关联,忽略了属性所属的节点层级路径,导致不同节点下的同名属性(如多个id、title)被错误匹配,出现重复结果,无法准确区分差异。

修正后的解决方案

方案1:准确对比属性差异(含节点路径)

通过生成完整节点路径+属性名作为唯一标识,确保不同节点下的同名属性能被正确匹配:

DECLARE @XML1 XML
DECLARE @XML2 XML

SET @XML1 = (SELECT TOP 1 Xml FROM TableA NOLOCK WHERE Id=22)
SET @XML2 = (SELECT TOP 1 Xml FROM TableB NOLOCK WHERE Id=34)

;WITH XML1 AS (
    SELECT 
        -- 生成属性的完整层级路径(如section/oper-ins-procref/qualifier/builddates/id)
        T.N.value('string-join(ancestor-or-self::*/local-name(.), ''/'')', 'nvarchar(500)') AS FullPropertyPath,
        T.N.value('local-name(.)', 'nvarchar(100)') AS NodeName,
        T.N.value('.', 'nvarchar(100)') AS Value
    FROM @XML1.nodes('//@*') AS T(N)
), XML2 AS (
    SELECT 
        T.N.value('string-join(ancestor-or-self::*/local-name(.), ''/'')', 'nvarchar(500)') AS FullPropertyPath,
        T.N.value('local-name(.)', 'nvarchar(100)') AS NodeName,
        T.N.value('.', 'nvarchar(100)') AS Value
    FROM @XML2.nodes('//@*') AS T(N)
)
SELECT 
    COALESCE(XML1.FullPropertyPath, XML2.FullPropertyPath) AS FullPropertyPath,
    COALESCE(XML1.NodeName, XML2.NodeName) AS NodeName,
    XML1.Value AS Value1,
    XML2.Value AS Value2,
    -- 明确标记差异类型
    CASE
        WHEN XML1.FullPropertyPath IS NULL THEN '新增属性'
        WHEN XML2.FullPropertyPath IS NULL THEN '删除属性'
        ELSE '属性值修改'
    END AS DiffType
FROM XML1
FULL OUTER JOIN XML2 
    ON XML1.FullPropertyPath = XML2.FullPropertyPath
WHERE 
    COALESCE(XML1.Value, '') <> COALESCE(XML2.Value, '')
    OR XML1.FullPropertyPath IS NULL
    OR XML2.FullPropertyPath IS NULL

方案2:扩展对比元素节点的新增/删除

如果需要识别元素节点本身的新增或删除,可添加以下查询:

;WITH XML1_Elements AS (
    SELECT 
        T.N.value('string-join(ancestor-or-self::*/local-name(.), ''/'')', 'nvarchar(500)') AS ElementPath,
        T.N.value('local-name(.)', 'nvarchar(100)') AS ElementName
    FROM @XML1.nodes('//*') AS T(N)
), XML2_Elements AS (
    SELECT 
        T.N.value('string-join(ancestor-or-self::*/local-name(.), ''/'')', 'nvarchar(500)') AS ElementPath,
        T.N.value('local-name(.)', 'nvarchar(100)') AS ElementName
    FROM @XML2.nodes('//*') AS T(N)
)
SELECT 
    COALESCE(XML1_Elements.ElementPath, XML2_Elements.ElementPath) AS ElementPath,
    COALESCE(XML1_Elements.ElementName, XML2_Elements.ElementName) AS ElementName,
    CASE
        WHEN XML1_Elements.ElementPath IS NULL THEN '新增元素节点'
        WHEN XML2_Elements.ElementPath IS NULL THEN '删除元素节点'
    END AS DiffType
FROM XML1_Elements
FULL OUTER JOIN XML2_Elements 
    ON XML1_Elements.ElementPath = XML2_Elements.ElementPath
WHERE 
    XML1_Elements.ElementPath IS NULL
    OR XML2_Elements.ElementPath IS NULL

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 08:15:34