有一种简单的方法可以从两个整数的复合键创建一个唯一的整数键吗?

时间:2009-11-16 21:43:34

标签: tsql math primary-key uniqueidentifier composite-key

由于与问题没有太大关系的各种原因,我有一个带有由两个整数组成的复合键的表,我想从这两个数字中创建一个唯一的键。我最初的想法只是将它们连接起来,但当我意识到(51,1)的复合键会产生与(5,11)相同的唯一键时,我很快就遇到了问题,即511.

有没有人有一种聪明的方法来生成两个整数中的整数,这样生成的整数对于这对起始整数是唯一的?

编辑在面对大量的数学后,我意识到我应该包含的一个细节是所讨论的密钥的大小。在始发对中,第一个密钥当前是6位数,并且在系统的生命周期中可能保持7位数;第二个关键还没有超过20个。鉴于这些限制,看起来问题就不那么令人生畏了。

9 个答案:

答案 0 :(得分:21)

如果您希望生成的密钥包含与其两个组件相同的位数,则可以在数学上证明这是不可能的。但是,如果从两个32位整数开始,并且可以使用64位int作为结果,那么显然可以这样做:

key1 << 32 | key2

答案 1 :(得分:4)

已经在相当多的细节中对此进行了讨论(正如递归所说,输出必须包含比单个输入更多的位)。

Mapping two integers to one, in a unique and deterministic way

How to use two numbers as a Map key

http://en.wikipedia.org/wiki/Cantor_pairing_function#Cantor_pairing_function

答案 2 :(得分:2)

只有拥有其中一个键的上限时,才能执行此操作。假设您有key1key2,而up1key1永远不会达到的值,那么您可以将这些键组合起来:

combined = key2 * up1 + key1;

即使键理论上可以无限制地增长,通常也可以在实践中估计保存上限。

答案 3 :(得分:2)

将一个具有足够高的值乘以

SELECT id1 * 1000000 + id2

或使用文字连接:

SELECT CAST(CAST(id1 AS nvarchar(10)) + RIGHT('000000' + CAST(id2 AS nvarchar(10)), 6) AS int)

或者跳过整数并将ID与非数字分隔:

SELECT CAST(id1 AS nvarchar) + ':' + CAST(id2 AS nvarchar)

答案 4 :(得分:1)

这两个建议的解决方案都需要一些关于接受密钥范围的知识。

为避免做出这种假设,可以将数字混合在一起。

Key1 = ABC => Digits = A, B, C
Key2 = 123 => Digits = 1, 2, 3
Riffle(Key1, Key2) = A, 1, B, 2, C, 3

当没有足够的数字时,可以使用零填充:

Key1 = 12345, Key2 = 1 => 1020304051

此方法还可以推广任意数量的键。

答案 5 :(得分:1)

由于我喜欢你问题的理论方面(它真的很漂亮),并且与许多实际答案所说的相矛盾,我想回答你标签的“数学”部分:)

实际上可能将任意两个数字(或实际上任何数字系列)映射到一个数字。这被称为Gödel number,最初由KurtGödel于1931年出版。

用你的问题快速举例说明;说我们有两个变量v1和v2。然后v3 = 2 v1 * 3 v2 将给出唯一的数字。此编号还唯一标识v1和v2。

当然,得到的数字v3可能会不合需要地快速增长。请把这个答案作为对你问题中理论方面的回答。

答案 6 :(得分:1)

为mysql编写了这些可以正常工作的

CREATE FUNCTION pair(x BIGINT unsigned,y BIGINT unsigned) 返回BIGINT无符号确定性 返回((x + y)*(x + y + 1))/ 2 + y;

CREATE FUNCTION reversePairX(z BIGINT unsigned) 返回BIGINT无符号确定性 返回(FLOOR(( - 1 + SQRT(1 + 8 * z))/ 2))*((FLOOR(( - 1 + SQRT(1 + 8 * z))/ 2))+ 3)/ 2 - z ;

CREATE FUNCTION reversePairY(z BIGINT unsigned) 返回BIGINT无符号确定性 返回z - (FLOOR(( - 1 + SQRT(1 + 8 * z))/ 2))*((FLOOR(( - 1 + SQRT(1 + 8 * z))/ 2))+ 1)/ 2 ;

答案 7 :(得分:0)

冒着听起来很滑稽的风险:

NewKey = fn(OldKey1, OldKey2)

其中fn()是一个函数,用于从添加到现有表中的列中查找新的自动编号键值。

显然,两个整数字段可以比单个整数字段保持指数多的值。

答案 8 :(得分:0)

为什么不直接使用ROW_NUMBER()或IDENTITY(int,1,1)来设置新ID?他们真的需要关系吗?