【问题标题】:How can I compare two xml in sql with nodes that has occurrences?如何将 sql 中的两个 xml 与出现的节点进行比较?
【发布时间】:2021-12-22 07:35:49
【问题描述】:

我需要知道原始 xml 是否有节点出现,以及我想要比较的第二个 xml。可以进行比较。

<!-------Original xml----->

<datos>      
      <clave1>017</clave1>
      <clave2>017</clave2>
      <clave3>017</clave3>      
      <Datos2>
        <f>
          <color>1</color>
          <color1>999</color1>   
        </f>
        <f>
          <color>0</color>
          <color1>003</color1>          
        </f>
      </Datos2>      
</datos>

 <!-------second xml that i make change, it could have more occurrences in node f  ----->

<datos>      
      <clave1>017</clave1>
      <clave2>017</clave2>
      <clave3>017</clave3>      
      <Datos2>
        <f>
          <color>2</color>
          <color1>566</color1>   
        </f>
        <f>
          <color>0</color>
          <color1>003</color1>          
        </f>
      </Datos2>      
</datos>

【问题讨论】:

  • 需要样本数据和所需结果。
  • 好的。我更新了我的问题主体。我们可以在第二个 xml 中看到第二次出现的数据发生了变化,我该如何在 sql 中进行操作,它也可能有更多的出现,并且需要知道发生了什么变化。
  • 提问时,您需要提供minimal reproducible example: (1) DDL 和样本数据填充,即 CREATE 表和 INSERT T-SQL 语句。 (2) 你需要做什么,即逻辑和你的代码尝试在 T-SQL 中实现它。 (3) 期望的输出,基于上述#1 中的样本数据。 (4) 您的 SQL Server 版本 (SELECT @@version;)。
  • 它可以有 less f 节点,还是只有更多或更改的节点?是否只有colorcolor1 子节点?

标签: sql sql-server xml database tsql


【解决方案1】:

您可以使用类似的方法来比较同一索引处的f 节点

SELECT
  v2.pos,
  colorFirst = v1.color,
  color1First = v1.color1,
  colorSecond = v2.color,
  color1Second = v2.color1
FROM @xml2.nodes('/datos/Datos2/f') x2(f)
CROSS JOIN @xml1.nodes('/datos/Datos2') x1D(datos2)
CROSS APPLY (VALUES(
    x2.f.value('let $i:= . return count(../f[. << $i]) + 1','int'),
    x2.f.value('(color/text())[1]','varchar(10)'),
    x2.f.value('(color1/text())[1]','varchar(10)')
)) v2(pos, color, color1)
OUTER APPLY x1D.datos2.nodes('f[sql:column("v2.pos")]') x1(f)
CROSS APPLY (VALUES(
    x1.f.value('(color/text())[1]','varchar(10)'),
    x1.f.value('(color1/text())[1]','varchar(10)')
)) v1(color, color1)
WHERE (v1.color IS NULL OR v1.color <> v2.color)
   OR (v1.color1 IS NULL OR v1.color1 <> v2.color1);

db<>fiddle

SQL Server 不支持fn:position(),所以我们需要通过计算以前的节点来破解它。

【讨论】:

    【解决方案2】:

    这是一个更通用的解决方案,如果您需要更多子级别,则需要进行一些调整,或者如果您在其他 xml 上有更多数据,则可能需要“完全外部连接”

    select a.position,a.path,a.value ,b.value 
    ,[change] = case when b.value = a.value then '' else '*' end
    from(
    select 
         [path]     = '/'+a.value('local-name(.)','varchar(max)') + ISNULL('/'+sub1Name,'') + ISNULL('/'+sub2Name,'')+ ISNULL('/'+sub3Name,'')
        ,[value]    = ISNULL(sub3Value, ISNULL(sub2Value, sub1Value))
        ,[position] = p1*100 + isnull(p2*10 /*max 10 sub levels*/,0) + isnull(p3,0)/*max 10 sub levels*/
    from @xml1.nodes('/*') a(a)
    /* 3 sub levels --duplicate if more xml sub levels needed */
    outer apply(select [p1]=ROW_NUMBER()over(order by (select null)),b.query('./*'), b.value('local-name(.)','varchar(max)'),b.value('.','varchar(max)')  from    a.nodes('./*') b(b)) b(p1,sub1,sub1Name,sub1Value)
    outer apply(select [p2]=ROW_NUMBER()over(order by (select null)),c.query('./*'), c.value('local-name(.)','varchar(max)'),c.value('.','varchar(max)')  from sub1.nodes('./*') c(c)) c(p2,sub2,sub2Name,sub2Value)
    outer apply(select [p3]=ROW_NUMBER()over(order by (select null)),d.query('./*'), d.value('local-name(.)','varchar(max)'),d.value('.','varchar(max)')  from sub2.nodes('./*') d(d)) d(p3,sub3,sub3Name,sub3Value)
    )a
    left join(
    select 
         [path]     = '/'+a.value('local-name(.)','varchar(max)') + ISNULL('/'+sub1Name,'') + ISNULL('/'+sub2Name,'')+ ISNULL('/'+sub3Name,'')
        ,[value]    = ISNULL(sub3Value, ISNULL(sub2Value, sub1Value))
        ,[position] = p1*100 + isnull(p2*10,0) + isnull(p3,0)
    from @xml2.nodes('/*')  a(a)
    outer apply(select [p1]=ROW_NUMBER()over(order by (select null)),b.query('./*'), b.value('local-name(.)','varchar(max)'),b.value('.','varchar(max)')  from    a.nodes('./*') b(b)) b(p1,sub1,sub1Name,sub1Value)
    outer apply(select [p2]=ROW_NUMBER()over(order by (select null)),c.query('./*'), c.value('local-name(.)','varchar(max)'),c.value('.','varchar(max)')  from sub1.nodes('./*') c(c)) c(p2,sub2,sub2Name,sub2Value)
    outer apply(select [p3]=ROW_NUMBER()over(order by (select null)),d.query('./*'), d.value('local-name(.)','varchar(max)'),d.value('.','varchar(max)')  from sub2.nodes('./*') d(d)) d(p3,sub3,sub3Name,sub3Value)
    )b on b.path = a.path and a.position = b.position
    where b.value <> a.value
    

    db<>fiddle

    position full path value1 value2
    411 /datos/Datos2/f/color 1 2
    412 /datos/Datos2/f/color1 999 566

    【讨论】:

      猜你喜欢
      • 2012-08-21
      • 1970-01-01
      • 2021-02-04
      • 2013-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-27
      • 2019-05-21
      相关资源
      最近更新 更多