Oracle ORA_HASH功能。结果会是恒定的吗?[复制]

Oracle ORA_HASH function. Will the results be constant? [duplicate]

提问人:Johannes 提问时间:11/7/2023 更新时间:11/8/2023 访问量:76

问:

我们要求为ERP系统中的多个表提供唯一的(每个表)NUMERIC ID,因为与第三方系统的接口需要它。这些预言机表(包括 Objid)中的键通常是 Varchar2。如果可以阻止,我不想在 20+ 表中创建新列,所以我想知道ORA_HASH是否可以解决问题。

我的问题是: 如果我运行例如

Select  ora_hash(order_no || release_no || line_no)  from purchase_order_line

随着表中数据的变化,我是否总是为某个order_no获得相同的哈希值 ||release_no ||line_no组合?或者,如果表中的整体数据发生变化,则该哈希值在不同时间会有所不同。如果哈希值保持不变,我可以将其限制为第 3 方系统要求的 bigint 的最大大小,并始终为特定组合获得相同的唯一值,对吧?我的思路有什么问题吗? 如果我们切换到一个新的数据库,迁移我们所有的数据,会发生什么。我强烈认为,每次组合,我们会得到不同的数据集,对吧?

表中的行数不会超过 BIGINT 的最大值。

我在表中添加和删除数据时多次尝试查询,它总是返回相同的哈希值。

Select  ora_hash(order_no || release_no || line_no)  from purchase_order_line

我还快速检查了几个包含更多数据的表,我们似乎在主键上没有冲突。

SQL Oracle PLSQL 哈希

评论

0赞 jarlh 11/7/2023
恒定,是的。可能是独一无二的。
0赞 MT0 11/7/2023
ora_hash('ABC' || 'DEF' || 'GHI')将与 相同。这似乎是实现哈希的糟糕方法。ora_hash('A' || 'BCD' || 'EFGHI')
1赞 Koen Lostrie 11/7/2023
你想散列的任何具体原因吗?该函数sys_guid生成大而唯一的值。在我们的项目中,它们被用作生成的主键。如果每次迁移/合并多个数据集,则冲突的风险最小。当然,这确实需要改变数据模型。另一方面,20+张桌子似乎并不多。
1赞 Chris Saxon 11/7/2023
您可以使用以下命令将 guid 转换为十进制to_number ( sys_guid(), 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' )
1赞 Paul W 11/8/2023
您可以做得更好,而不是具有高冲突率。请考虑改为或 .你可以从中获得更多的独特性,以避免冲突,即使在较弱的算法下也是如此。您也可以使用更高位的算法。是的,返回值是确定性的,因此在给定相同的输入时,它们将始终相同。ORA_HASHdbms_crypto.hashSTANDARD_HASHSHA1

答:

0赞 Bogdan Dincescu 11/8/2023 #1

ora_hash可能会导致一些碰撞。最好使用standard_hash。这将返回原始数据,但不太可能发生冲突。而且,如果你对列的串联进行哈希处理,最好做类似 standard_hash(order_no ||'|'||release_no ||'|'||line_no,“MD5”)。将串联与 |以避免混淆,例如'ab'||'c' 给出与 'a'||' 相同的结果公元前'。

0赞 Thorsten Kettner 11/8/2023 #2

您需要向表添加唯一的数字 ID,并且您希望避免这种情况,因为您的表已经具有唯一的 ID,尽管是字母数字。您正在寻找一种方法来获取确定性数字 ID。

根据这些 ID 中允许的不同字符数以及这些 ID 可以变成多长时间,以下 PL/SQL 函数可能足以从字母数字 ID 获取到数字 ID。

CREATE OR REPLACE FUNCTION get_unique_id(p_objid VARCHAR2) RETURN INTEGER DETERMINISTIC
IS
  PRAGMA UDF;
  v_allowed  VARCHAR2(4000) := 'ABCDEDGIJKLMNOPQRSTUVWXYZ1234567890';
  v_factor   INTEGER     := 1;
  v_result   INTEGER     := 0;
BEGIN
  FOR i IN 1 .. LENGTHC(p_objid) LOOP
    v_result := v_result + INSTRC(v_allowed, SUBSTRC(p_objid, i, 1)) * v_factor;
    v_factor := v_factor * (LENGTHC(v_allowed) + 1);
  END LOOP;
  RETURN v_result;
END get_unique_id;

毕竟这很简单。您有一串允许的字符,用于从目标字符转换为数字。

假设允许有 99 个字符。objid 中的第一个字符将获得 1 到 99 之间的值,具体取决于其在允许的字符串中的位置。下一个字符将获得一个从 1 到 99 乘以 100 的值。等等。所有这些值的总和就是生成的数字 ID。 (当然,如果您觉得这更直观,则可以从右到左运行您的objid字符串。

如何测试功能:

select get_unique_id('0000000000') from dual;

将“00000000000”更改为对象中允许的最大字符数和v_allowed中最后一个允许字符的字符串。你得到结果还是异常?生成的数字是否太大,是否小到足以满足要求?


最后,看看(docs.oracle.com/../ORA_HASH.html)的文档:ORA_HASH

ORA_HASH(expr [,max_bucket])

expr 表示的数据长度没有限制

可选的 max_bucket 参数确定哈希函数返回的最大存储桶值。您可以指定介于 0 和 4294967295 之间的任何值。默认值为 4294967295。

这意味着输入值的数量不受限制,生成的哈希值数量有限。换句话说,不同的输入可以产生相同的输出。因此,您不能将此功能用于您的目的。对于您能找到的每个通用哈希函数也是如此。只有当编写哈希函数来为一组已知的、有限的输入数据生成唯一的结果时,它才适合您。好吧,在这种情况下,您可以在非泛型哈希函数之上调用我的函数。

关于确定性函数的注释:Oracle 知道明确声明为确定性的确定性函数,就像我声明我的函数一样。这些函数保证返回确定性结果,即相同的输入值始终具有相同的输出值。因此,如果是确定性的,这意味着当您在 Oracle 版本中使用它时,只要您使用相同的输入调用它,它就会可靠地产生相同的输出。无论您的 Oracle 版本是 12c、19c、21c 还是其他版本,都无关紧要。但是,不能保证 Oracle 永远不会更改算法。因此,虽然该函数在 Oracle 12c 中可能是确定性的,在 21c 中仍然是确定性的,但不能保证这两个版本会产生相同的结果。因此,除非 Oracle 明确说明,否则不能保证该函数在数据库版本之间具有确定性。ORA_HASH

评论

0赞 Johannes 11/8/2023
感谢您的长篇回答。此处允许的字符串为“ABCDEDGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890/+”。
0赞 Johannes 11/8/2023
我将单独查看表键,也许会找到一些可行的特定案例逻辑。否则,表中的额外列是最后的手段。西尔,感谢您的广泛回答
0赞 Johannes 11/8/2023
旧的消失评论:真的很有趣,我会保留这个以备将来使用:-)。运行快速测试,79301557162312826337222065954791(最多)20 个字符的 Objid 上的输出。正如我之前所说,从数学上讲,不可能创建一个通用的哈希函数来返回一个适合 BIGINT 的唯一数字 id,因为显然可能的组合数量要大得多(具有 20 个字符的字符串)。我已经放弃了使用哈希函数/自定义哈希函数的想法。也没有想到其他东西,因为由于显而易见的原因,我不能使用例如 Rownum。