如何使用SQL干净地输出表diff

问题描述 投票:0回答:2

我正在研究一种解决方案,以帮助比较两个表之间的数据。说我有这两张桌子

Table: Clients_old
----------------------------------------------
ClientID    FirstName   LastName        Age
----------------------------------------------
1           John        Doe             20
2           Jane        Doe             20

Table: Clients_updated
----------------------------------------------
ClientID    FirstName   LastName        Age
----------------------------------------------
1           John        Doe             99
2           Jane        Smith           99

现在,我正在使用EXCEPT和一些丑陋的案例逻辑来输出上面的差异。

---------------------------------------------------------------------------------------------------------
ClientID    FirstName_Old   FirstName_Updated   LastName_Old    LastName_Updated    Age_Old     Age_Updated
---------------------------------------------------------------------------------------------------------
1           NULL            NULL                NULL            NULL                20          99  
2           NULL            NULL                Doe             Smith               20          99

这是一个开始,但我想清理输出。鉴于这两个表,SQL中是否有一种方法可以获得这样的diff格式?

-----------------------------------------------------
ClientID    ColumnName      OldValue    UpdatedValue
-----------------------------------------------------
1           Age             20          99  
2           LastName        Doe         Smith
2           Age             20          99

我知道如何使用C#来实现它,但我很好奇是否有SQL解决方案。

sql sql-server compare
2个回答
2
投票

您可以在SQL Server中取消隐藏。为此,我建议:

with co as (
      select v.*
      from clients_old co cross apply
           (values (co.client_id, co.firstname, 'firstname'),
                   (co.client_id, co.lastname, 'lastname'),
                   (co.client_id, co.age, 'age')
           ) v(client_id, val, col)
     ),
     cu as (
      select v.*
      from clients_updated cu cross apply
           (values (cu.client_id, cu.firstname, 'firstname'),
                   (cu.client_id, cu.lastname, 'lastname'),
                   (cu.client_id, cu.age, 'age')
           ) v(client_id, val, col)
     )
select co.client_id, co.col, co.val as old_value, cu.val as updated_value
from co join
     cu
     on co.client_id = cu.client_id and co.col = cu.col and
        co.val <> cu.val;

如果val可以是NULL,那么逻辑会稍微复杂一些。

请注意,此方法确实假定列类型都是字符串。有很多方法可以解决这个问题,但代码有点复杂。


2
投票

戈登的解决方案将更具性能。

但是,这里有一种方法不是数据类型敏感的,而且更加动态,不必指定字段。

例子或dbFiddle

Select A.ClientID
      ,C.Field
      ,OldValue = max(case when Src='Old' then Value end)
      ,NewValue = max(case when Src='New' then Value end)
 From (
        Select Src='Old',* from Clients_old
        Union All
        Select Src='New',* from Clients_new
      ) A
 Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
 Cross Apply (
                Select Field = a.value('local-name(.)','varchar(100)')
                      ,Value = a.value('.','varchar(max)') 
                 From  B.XMLData.nodes('/row')  as C1(n)
                 Cross Apply C1.n.nodes('./@*') as C2(a)
                 Where a.value('local-name(.)','varchar(100)') not in ('Src','ClientID')
             ) C
 Group By A.ClientID,C.Field
 Having max(case when Src='Old' then Value end) <> max(case when Src='New' then Value end)
        or  count(*)=1

返回

ClientID    Field    OldValue   NewValue
1           Age      20         99
2           Age      20         99
2           LastName Doe        Smith
© www.soinside.com 2019 - 2024. All rights reserved.