【问题标题】:Get hash values from SQL Server and Oracle and compare them?从 SQL Server 和 Oracle 获取哈希值并进行比较?
【发布时间】:2012-02-07 06:56:50
【问题描述】:

是否可以从两个数据库服务器生成哈希码并进行比较?如何在 SQL Server 中编写以下伪 SQL?尤其是在 SQL server 和 oracle 中接受多个数字/浮点列的两个 getHash 函数。

select s.PK
from sqltable s
    join openquery(oracleLinkedServer, 
      'select PK, getHash(Column1, floatColumn2, ..., floatColumnN) oracleHash 
       from oracleTable') o on o.PK = s.PK
where
    getHash(Column1, floatColumn2, ..., floatColumnN) <> oracleHash

【问题讨论】:

  • 鉴于 SQL Server 和 Oracle 之间的许多数据类型不太可能有 100% 匹配的表示,它不太可能工作
  • 如果您将所有值转换为大字符串 (x, 1.234, 2.345 = "x1.2342.345"),然后根据相同的编码(例如 md5)计算哈希值,它可能会起作用。
  • @tbone 将它们连接成一个大字符串可能会带来更多的不确定性。
  • @Damien_The_Unbeliever 同意了。我这样做是为了避免由于连接速度慢而从远程服务器中提取太多数据。
  • @NickW 我相当确定,给定相同的输入字符串,md5 的 Oracle 实现将给出与 SQL Server 实现相同的 32 十六进制值(假设不使用随机数或随机盐)。我无权访问 SQL Server,但我会挑战你证明这是错误的。

标签: sql-server oracle hash


【解决方案1】:

在 SQL Server 中:

select upper(substring(sys.fn_sqlvarbasetostr(hashbytes('MD5','A')),3,32));

结果:

7FC56270E7A70FA81A5935B72EACBE29

在甲骨文中:

select rawtohex(
    DBMS_CRYPTO.Hash (
        UTL_I18N.STRING_TO_RAW ('A', 'AL32UTF8'),
        2)
    ) from dual;

结果:

7FC56270E7A70FA81A5935B72EACBE29

确保您的字符串完全相同(区分大小写)。这里我用'A'作为一个简单的例子,但它实际上可以是任何字符串。

如果您通过转换为大字符串来避免数据类型差异,您应该能够在不同平台上生成相同的 md5 哈希。请注意,SQL Server 在哈希前面添加了一个“0x”来表示十六进制表示,我用子字符串剥离了它。

【讨论】:

  • 谢谢。在我的情况下,我已经在 Oracle 端将 AL32UTF8 更改为 AL16UTF16LE 并将 MS SQL 端的文本转换为 nvarchar ( cast('ĄĘA' as nvarchar(max) )但无论如何非常感谢。
  • 如何获取整行而不是列的哈希?
  • @Aravindh 您可以连接字符串,但听起来您想要一行的唯一哈希/ID,这意味着您应该查看sys_guid
  • @tbone 就我而言,我想比较 sql server 和 oracle 之间的行,但我认为 GUID 不会有帮助,因为我也无法在 sql server 端生成它们?
  • @Aravindh 在这种情况下,选择最能代表行中唯一性的列并将它们连接成 1 个大字符串(大小合理,最多 4000 个)。当然,您会将日期转换为字符串。
【解决方案2】:

在 SQL Server 中,您有 hashbytes();在 Oracle 中,您有 DBMS_CRYPTO.Hash()。您应该能够使用它们来计算双方的 MD5 哈希值,尽管我不肯定哈希值会匹配......它值得一试。

还有其他方法可以比较表格,但要回答您的问题,这些是任一平台上的两个本机函数。

【讨论】:

  • 这两个函数不接受多列,因此用户必须将多列合并为一个。是的,正如您所提到的,即使它们的值相同,也不确定散列值是否匹配,尤其是对于这些浮点数。
  • @NickW 如果你想同时散列多个列,在 Oracle 上你可以使用 DBMS_SQLHASH:docs.oracle.com/cd/B19306_01/network.102/b14266/appendixb.htm 但我认为这个包不太可能匹配 SQL Server 的任何输出。跨度>
【解决方案3】:

您可以在 SQL Server 中使用CHECKSUM() 来计算多列哈希。

【讨论】:

    【解决方案4】:

    如果您对 Oracle 系统的访问权限有限,并且尚未被授予对 DBMS_CRYPTO.Hash() 的执行权限,您仍然可以使用 DBMS_OBFUSCATION_TOOLKIT.MD5() 在 Oracle 和 SQL Server 上生成相同的散列值。您只需确保将字符串转换为与运行 SQL Server 的机器相同的代码页。我相信大多数北美 Windows 机器上的默认值是 windows-1252 ANSI Latin 1; Western European (Windows)(来自 https://docs.microsoft.com/en-us/windows/desktop/intl/code-page-identifiers

    我不是特别喜欢在 WHERE 子句中调用函数,所以我将 SQL 端哈希放入 CTE,然后加入 OPENQUERY 结果。

    ;WITH sqlHash AS
    (
        SELECT s.PK
            ,UPPER(CONVERT(CHAR(32), HASHBYTES('MD5', s.Column1 + '|' + s.Column2 + '|' + s.ColumnN), 2)) AS sqlHash
        FROM sqltable s
    )
    SELECT s.PK
    FROM sqlHash s
    JOIN OPENQUERY
         ( oracleLinkedServer, '
         SELECT 
            PK
            ,CONVERT(UPPER(RAWTOHEX(
                SYS.DBMS_OBFUSCATION_TOOLKIT.MD5(input_string => "oraColumn1" || ''|'' || "oraColumn2" || ''|'' || "oraColumnN")
                )),''AL32UTF8'',''WE8MSWIN1252'') AS oracleHash
           FROM oracleTable' ) o ON o.PK = s.PK
    WHERE s.sqlHash <> o.oracleHash
    

    这里需要指出的一些事情可能不会立即显现出来。

    首先,我在连接文本的每一列之间添加一个竖线字符|。这是区分 "digital" + "aaron""digi" + "talaaron" 等组合与产生相同哈希的区别,因为字符串将变为 "digital|aaron""digi|talaaron",它们应该产生不同的哈希值。

    其次,当我们在 CTE 中获取 SQL 端时,该值返回为 VARBINARY。如果我们只显示这个值,就会有一个前导 0x。但我们想去掉它,并将值转换为比VARBINARY 更有用的东西。所以我们转换为CHAR(32),样式为2

    【讨论】:

      猜你喜欢
      • 2018-09-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-25
      相关资源
      最近更新 更多