比较数据库中的 XML 值适用于更改但不适用于添加

Comparing XML values in database works for changes but not additions

我正在尝试比较 XML 数据以确定已更改或添加的内容。如果更改了单个记录,或者以相同的顺序发生了多个更改,则它 returns 是预期的结果。但是,如果添加了新记录,它不会按预期工作。

我遇到的问题是数据可能来自许多地方,因此数据中没有一致的 table 名称、ID 或其他内容。然而 XML 将始终有一个 <original><changed> 节点,其中将包含查询结果,如下所示:

@xmlOriginal =SELECT * FROM Table WHERE ID=@ID FOR XML AUTO, ELEMENTS XSINIL, ROOT('Original'))
--INSERT query goes here
@xmlChanged = SELECT * FROM TABLE WHERE ID=@ID FOR XML AUTO, ELEMENTS XSINIL, ROOT('Changed'))
SET @xml = (SELECT @xmlOriginal, @xmlChanged for XML Path('Update'))

有没有办法智能地找出什么是 changed/added?我只需要知道原始值与新值,不管是否有原始值。

我在下面创建了一个简单示例,该示例从一行开始,然后添加另一行。当我运行 我的SQL 看到变化时,它显示了错误的信息。为了清楚起见,我也包括了实际和期望的结果。

DECLARE @Audit TABLE (
    id int not null,
    Details xml 
)

INSERT INTO @Audit (ID,details) 
Values (1, 
'<Update>
  <Original xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <dbo.MyTable>
      <ID>E99C0245-1A06-EA11-A836-00155DB6D822</ID>
      <Amount>1234.00</Amount>
    </dbo.MyTable>
  </Original>
  <Changed xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <dbo.MyTable>
      <ID>E99C0245-1A06-EA11-A836-00155DB6D822</ID>
      <Amount>1234.00</Amount>
    </dbo.MyTable>
    <dbo.MyTable>
    <ID>D74B6DED-1B06-EA11-A836-00155DB6D822</ID>
      <Amount>555.00</Amount>  
    </dbo.MyTable>
  </Changed>
</Update>')

DECLARE @XML XML = (SELECT Details FROM @Audit WHERE ID = 1)

;WITH 
XMLOriginal AS
    (
      SELECT T.N.value('local-name(.)', 'nvarchar(100)') as Field,
             T.N.value('.', 'nvarchar(1000)') as VALUE
      FROM @XML.nodes('Update/Original/*/*') as T(N)
    ),
XMLChanged as
    (
      SELECT T.N.value('local-name(.)', 'nvarchar(100)') as Field,
             T.N.value('.', 'nvarchar(1000)') as VALUE
      FROM @XML.nodes('Update/Changed/*/*') as T(N)
    )

    SELECT   
        COALESCE(XMLOriginal.Field, XMLChanged.Field) as Field 
        ,XMLOriginal.VALUE as Original
        ,XMLChanged.VALUE as Changed
FROM 
    XMLOriginal
FULL OUTER JOIN XMLChanged
    ON XMLOriginal.Field = XMLChanged.Field
LEFT OUTER JOIN @Audit L 
        ON L.ID = 1
    WHERE COALESCE(XMLOriginal.VALUE, '') <> COALESCE(XMLChanged.VALUE, '')   

输出:

+--------+--------------------------------------+--------------------------------------+
| FIELD  | ORIGINAL                             | CHANGED                              |
+--------+--------------------------------------+--------------------------------------+
| Amount | 1234.00                              | 555.00                               |
+--------+--------------------------------------+--------------------------------------+
| ID     | E99C0245-1A06-EA11-A836-00155DB6D822 | D74B6DED-1B06-EA11-A836-00155DB6D822 |
+--------+--------------------------------------+--------------------------------------+

期望的输出:

+--------+--------------------------------------+--------------------------------------+
| FIELD  | ORIGINAL                             | CHANGED                              |
+--------+--------------------------------------+--------------------------------------+
| Amount | 1234.00                              | 1234.00                              |
+--------+--------------------------------------+--------------------------------------+
| ID     | E99C0245-1A06-EA11-A836-00155DB6D822 | E99C0245-1A06-EA11-A836-00155DB6D822 |
+--------+--------------------------------------+--------------------------------------+
| Amount |  NULL                                | 555.00                               |
+--------+--------------------------------------+--------------------------------------+
| ID     |  NULL                                | D74B6DED-1B06-EA11-A836-00155DB6D822 |
+--------+--------------------------------------+--------------------------------------+

我认为你很接近——根据我的经验,很难使用完全外部连接并获得有用的结果——我喜欢只使用左连接并使用一组已知的键连接到两侧。 .. 像这样(考虑到您的 CTE)

SELECT BASE.Field,
       O.VALUE AS Original_Value
       C.VALUE AS Changed_Value
FROM (SELECT  Field FROM XMLOriginal 
      UNION 
      SELECT Field FROM XMLChanged) AS BASE
LEFT JOIN XMLOriginal AS O ON BASE.Field = O.Field
LEFT JOIN XMLChanged AS  C ON BASE.Field = C.Field