【问题标题】:Is there a simple way to create a unique integer key from a two-integer composite key?有没有一种简单的方法可以从两个整数复合键创建一个唯一的整数键?
【发布时间】:2010-12-17 05:35:53
【问题描述】:

由于与问题不太相关的各种原因,我有一个表,其中包含由两个整数组成的复合键,我想从这两个数字中创建一个唯一键。我最初的想法是把它们串联起来,但是当我意识到 (51,1) 的复合键会产生与 (5,11) 相同的唯一键,即 511 时,我很快遇到了问题。

有没有人有聪明的方法从两个整数中生成一个整数,使得生成的整数对于一对起始整数是唯一的?

编辑:在面对大量数学问题后,我意识到我应该包括的一个细节是相关键的大小。在原始对中,第一个密钥当前为 6 位,并且可能在系统生命周期内保持 7 位;第二个键尚未大于 20。鉴于这些限制,看起来问题不那么令人生畏了。

【问题讨论】:

  • 没有 DBA 应该让您侥幸逃脱 - 如果需要,创建一个主键列并在两列上使用唯一约束
  • 查看 Matt Ball 的重复答案

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


【解决方案1】:

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

key1 << 32 | key2

SQL 语法

SELECT key1 * POWER(2, 32) + key2

【讨论】:

  • 这个。当然,请确保对两个整数进行完整性检查,以确保它们都是 32 位。 (假设您使用有符号整数,它们需要小于 2^31 或 2,147,483,648)。
  • 遗憾的是,我在 T-SQL 中执行此操作并且缺少位移运算符。
  • 然后用乘法假装那个妈妈。 :-) 我认为“key1 * 2^32”完成了同样的事情,但我的数学有点生疏了。
  • 来自dbaspot.com/forums/sqlserver-programming/…,实现@i &lt;&lt; @jSELECT @i * POWER(2, @j)
  • +1 即使 key1 是 32 位整数,那么 key1 &lt;&lt; 32 == 0 (您应该先将其转换为 64 位)
【解决方案2】:

为mysql写了这些,它们工作正常

创建函数pair(x BIGINT 无符号,y BIGINT 无符号) 返回 BIGINT 无符号 DETERMINISTIC 返回 ((x + y) * (x + y + 1)) / 2 + y;

创建函数reversePairX(z BIGINT 无符号) 返回 BIGINT 无符号 DETERMINISTIC 返回 (FLOOR((-1 + SQRT(1 + 8 * z))/2)) * ((FLOOR((-1 + SQRT(1 + 8 * z))/2)) + 3) / 2 - z ;

创建函数reversePairY(z BIGINT 无符号) 返回 BIGINT 无符号 DETERMINISTIC 返回 z - (FLOOR((-1 + SQRT(1 + 8 * z))/2)) * ((FLOOR((-1 + SQRT(1 + 8 * z))/2)) + 1) / 2 ;

【讨论】:

    【解决方案3】:

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

    事实上有可能将任意两个数字(或实际上任何一系列数字)映射到一个数字。这被称为Gödel number,由 Kurt Gödel 于 1931 年首​​次发表。

    举一个简单的例子,你的问题;假设我们有两个变量 v1 和 v2。然后 v3=2v1*3v2 将给出一个唯一编号。此编号还唯一标识 v1 和 v2。

    当然,生成的数字 v3 可能会增长得过快。请将此答案作为对您问题中理论方面的答复。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        这两种建议的解决方案都需要对可接受的密钥范围有所了解。

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

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

        当数字不够时可以使用零填充:

        Key1 = 12345, Key2 = 1 =&gt; 1020304051

        此方法也适用于任意数量的键。

        【讨论】:

          【解决方案6】:

          乘以一个足够高的值

          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)
          

          【讨论】:

            【解决方案7】:

            只有当你有一个键的上限时,你才能这样做。假设您有key1key2,而up1key1 永远无法达到的值,那么您可以像这样组合键:

            combined = key2 * up1 + key1;
            

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

            【讨论】:

            • 我喜欢,比我的回答更干净。只需确保您始终按预定义的顺序“编码”密钥并以相同的顺序“解码”它们。
            【解决方案8】:

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

            NewKey = fn(OldKey1, OldKey2)
            

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

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

            【讨论】:

              【解决方案9】:

              这已经进行了相当详细的讨论(但是,正如递归所说,输出必须包含比单个输入更多的位)。

              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

              【讨论】:

              • 相当复杂。这些答案让我失去了“双射”。 :p
              • @Tchalvak:如果你不是一个数学家,那就继续在维基百科中搜索你不知道的术语吧! (就个人而言,我真的很喜欢用这种富有成效的拖延来“教育”自己。)它归结为非常简单的东西。使用花哨的数学单词只是保持定义简洁准确。
              猜你喜欢
              • 1970-01-01
              • 2010-09-24
              • 2021-06-16
              • 2011-05-14
              • 2019-08-29
              • 2013-12-21
              • 1970-01-01
              • 2011-02-16
              • 2010-09-06
              相关资源
              最近更新 更多