【问题标题】:Compare TWO tables with same schema for data differences比较具有相同架构的两个表的数据差异
【发布时间】:2016-02-19 19:38:57
【问题描述】:

我正在处理一个典型的问题,我需要比较两个具有完全相同架构的表的数据差异。 假设数据库为 MS SQL 或 ORACLE。

更准确地说,这是我想要实现的目标:

  1. 我有一个包含一些数据的表 ORG
  2. 我正在创建表 ORG 的副本作为 BACKUP
  3. 现在我想更新表 ORG 的一些特定行中的一些特定列。

找出表 BACKUP 和 ORG 之间区别的最简单有效的方法是什么?

我看到的选项很少,例如使用 UNIONS、PIVOT、UNPIVOT 等。但我很困惑,需要一些有关最佳方法的指导。

谢谢,

【问题讨论】:

  • 您是否要生成差异报告?或者从源表更新目标表?您实际使用的是这两个数据库中的哪一个? SQL 可能会有所不同。 update 绝对不同。
  • 如果表有主键或唯一键约束会有所帮助。
  • 您好,该表没有任何主键。我尝试使用 JOIN,它似乎涵盖了我感兴趣的大部分场景。这就是我所做的 SELECT count(*) FROM ORG x INNER JOIN BACKUP q on p.city = q.city
  • 如果表没有任何主键,那么你也应该注意重复行。
  • 您选择的inner join 仅按列city 进行比较。其他列的差异如何?如果更新了不止一列,您将如何加入?

标签: sql-server oracle compare schema


【解决方案1】:

如果问题是检查两个表的身份,即答案是yes - 表包含 相同的数据或no - 有区别,还有另一种方法,在关系数据库中不太常见, 但最先进的文件。

这与检查下载文件的“正确性”(即与原始文件的身份)相同的方法 使用哈希码。如果您看到相同的哈希码,则答案是 yes 您拥有正确的文件。

如何将这种方法转换为关系数据库?

Oracle 提供函数standard_hash 计算字符串的MD5 哈希码

 select standard_hash('foo bar', 'MD5') hash_code from dual; 

 HASH_CODE                      
 --------------------------------
 327B6F07435811239BC47E1544353273 

因此您可以比较列值。 函数standard_hash 也适用于CLOBs,所以(理论上)你可以连接 列和行并计算整个表的哈希码。但这不是正确的方法,请记住 文件的哈希码是通过组合行的哈希码递增计算的。

这里是如何使用Java类java.security.MessageDigest计算MD5哈希码的演示 我正在使用 Groovy 脚本,因为不幸的是,这在 PL/SQL 中是不可能的。

 MessageDigest digest = MessageDigest.getInstance("MD5") 
 byte[] md5hash 

 groovyCon.eachRow ('select txt from MY_TABLE order by id') 
    {
       digest.update(it.txt.getBytes(StandardCharsets.UTF_8))
    }   

 md5hash = digest.digest();
 println md5hash.encodeHex().toString()

脚本启动哈希码,然后遍历行和updates 哈希码,最后 将其预设为字符串。这是处理文件时的典型方法,其中行的顺序很重要。在关系表中 订单未定义。您认为该表与 (A,B,C) 和 (C,B,A) 相同。

请参阅here 讨论如何使用XOR 将哈希码组合成与顺序无关的事项。

这里是两个字符串的哈希码组合的例子

 select UTL_RAW.BIT_XOR(standard_hash('foo', 'MD5'), standard_hash('bar', 'MD5')) hash_code from dual;

 HASH_CODE                      
 --------------------------------
 9B0805C206B7EBB8B6B9931D83E9F52A 

这种方法有一个很大的优势,它可以使用 PL/SQL 来实现。请参阅此处的示例 PL/SQL 的实现aggregate function MD5_XOR 计算整个表的 MD5 哈希码。

 select   MD5_XOR(txt) hash_code, count(*) cnt 
 from MY_TABLE;

 HASH_CODE                          CNT
 --------------------------------   -------
 173F1F8F85F1A154044B7629A23E949C   102 

当然你也可以把参数串联起来计算整张表的哈希码

 select MD5_XOR(to_char(id)||COL_TXT|| to_char(COL_DATE,'dd.mm.yyyy hh24:mi:ss')) md5  from  MY_TABLE;

或者您可以对表格的某些部分使用GROUP BY,看看哪些组是相同的,哪些是不同的。

好多了,如果这个聚合函数由Oracle原生实现,性能会大放异彩,会好很多 使用基于集合操作的SQL比较(需要对表进行排序)。 XOR 组合不需要 sort 并具有O(N) 复杂性,但用户实现会受到上下文切换的影响。

请参阅here 使其成为 Oracle 本地实现的想法。

【讨论】:

    【解决方案2】:

    您可以使用 SET 运算符 MINUS/INTERSECT,具体取决于您想要什么、两个表中的行之间的差异或匹配。

    要获得差异,请使用 MINUS

    SELECT <here_goes_your_column_list> FROM org
    MINUS
    SELECT <here_goes_your_column_list> FROM backup
    

    要获得匹配,请使用 INTERSECT

    SELECT <here_goes_your_column_list> FROM org
    INTERSECT
    SELECT <here_goes_your_column_list> FROM backup
    

    documentation查看更多详情。

    【讨论】:

    • 谢谢。我尝试加入,它似乎工作。如果有任何进一步的改进,我将探讨您提供的选项。
    • @Krishna 没问题。如果答案有帮助,请将其标记为已回答,也会对其他人有所帮助!
    • 我已经在 MSSQL 上尝试过这个解决方案,并且发现 Oracle 上的减号与 MSSQL 上的相同。我需要具有不同数据的列,与上面提到的相同。这可以在 MSSQL 上实现吗?
    【解决方案3】:

    试试这个

    -- compare tables data and data2
    with data as
     (select 1 as id, 'A' as val
        from dual
      union
      select 2, 'B' from dual),
    data2 as
     (select 1 as id, 'A' as val
        from dual
      union
      select 2, 'BC' from dual)
    
     -- data not in data2 and data2 not in data:
     (select *
        from data2
      minus
      select * from data) union all
     (select *
        from data
      minus
      select * from data2);
    

    【讨论】:

    • 谢谢。我尝试加入,它似乎工作。如果有任何进一步的改进,我将探讨您提供的选项。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-02
    • 1970-01-01
    • 1970-01-01
    • 2020-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多