用随机数替换序列

时间:2009-11-06 12:35:55

标签: sql postgresql random

我想用我自己定制的id生成器替换我在postgresql db中用于id的一些序列。生成器将生成一个随机数,最后带有一个checkdigit。所以这个:

SELECT nextval('customers')

会被这样的东西取代:

SELECT get_new_rand_id('customer')

该函数将返回一个数值,例如:[1-9][0-9]{9},其中最后一位是校验和。

我担心的是:

  1. 如何使事物成为原子
  2. 我如何避免两次返回相同的ID(这可以通过尝试将其插入具有唯一约束的列中来捕获,但我认为它已经迟到了)
  3. 这根本不是一个好主意吗?
  4. 注1 :我不想使用uuid,因为它要与客户沟通,10个数字比36个字符的uuid更容易沟通。

    Note2 :很少使用SELECT get_new_rand_id()调用该函数,但会在id-column而不是nextval()上将其指定为默认值。

    编辑:好的,下面的讨论很好!以下是为什么

    的一些解释
    1. 那么为什么我会用这种方式过度复杂? purpouse是隐藏客户的主键。

        

      我给每个新客户一个独特的   customerId(生成的序列号)   数据库)。因为我沟通了   与客户的号码是一个   我的竞争对手相当简单的任务   监督我的业务(有   其他数字,如发票nr和   订单nr具有相同的   属性)。正是这种监控我   想做一点点   更难(注意:并非不可能,但是   硬)。

    2. 为什么是校验位?

        

      在有任何关于隐藏序列号的讨论之前,我在ordernr中添加了一个checkdigit,因为在制作中的某些点上有klumbsy手指,我的想法是这将是一个很好的做法,以便将来保留。

    3. 在阅读讨论后,我当然可以看到我的方法不是解决问题的最佳方法,但我对如何解决它没有其他好处,所以请在这里帮助我。

      1. 我应该添加一个额外的列,我将我公开的ID提供给客户并将序列号作为主键吗?
      2. 如何以健全有效的方式生成要公开的ID?
      3. 必须使用checkdigit吗?

6 个答案:

答案 0 :(得分:18)

为了从序列生成唯一且随机的标识符,使用密码可能是个好主意。由于它们的输出是双射的(输入和输出值之间存在一对一的映射) - 与哈希不同,您将没有任何冲突。这意味着您的标识符不必像哈希一样长。

大多数加密密码都适用于64位或更大的块,但PostgreSQL wiki具有example PL/pgSQL procedure for a "non-cryptographic" cipher函数,可以在(32位)int类型上运行。免责声明:我自己没有尝试过使用此功能。

要将其用作主键,请从Wiki页面运行CREATE FUNCTION调用,然后在表上执行:

ALTER TABLE foo ALTER COLUMN foo_id SET DEFAULT pseudo_encrypt(nextval('foo_foo_id_seq')::int);

瞧!

pg=> insert into foo (foo_id) values(default);
pg=> insert into foo (foo_id) values(default);
pg=> insert into foo (foo_id) values(default);
pg=> select * from foo;
  foo_id   
------------
 1241588087
 1500453386
 1755259484
(4 rows)

答案 1 :(得分:3)

我在你的问题中添加了评论,然后意识到我应该更好地解释自己......我的道歉。

您可以拥有第二个密钥 - 而不是主密钥 - 对用户可见。该密钥可以使用primary作为您描述的哈希函数的种子,并且可以用于执行查找。该密钥将在插入后由触发器生成(这比尝试确保操作的原子性要简单得多)和

这是您与客户分享的关键,而不是PK。我知道如果PK对于用户应用程序是不可见的,那么存在争议(尽管我无法理解为什么)。现代数据库设计实践和我的个人经验似乎都暗示用户不应该看到PK。他们往往会给他们带来意义,随着时间的推移,这是一件非常糟糕的事情 - 无论他们是否在钥匙上都有一个校验位。

您的联接仍将使用PK完成。这个其他生成的密钥应该用于客户端查找。他们是面子,PK是胆量。

希望有所帮助。

编辑:FWIW,在数据库设计中几乎没有关于“正确”或“错误”的说法。有时它归结为一种选择。我认为通过单独使用PK并创建辅助密钥可以更好地满足您的选择 - 就是这样。

答案 2 :(得分:2)

我认为你过于复杂了。为什么不让数据库做它最擅长的事情并让它处理原子性并确保相同的id不被使用两次?为什么不使用postgresql SERIAL类型并获得自动生成的代理主键,就像SQL Server或DB2中的整数IDENTITY列一样?而是在列上使用它。此外,它将比您的用户定义的功能更快。

我同意隐藏此代理主键并使用公开的辅助密钥(对其具有唯一约束)来查找界面中的客户端。

您是否正在使用序列,因为您需要在多个表中使用唯一标识符?这通常表明您需要重新考虑您的表设计,并且这些表可能应该合并为一个表,并使用自动生成的代理主键。

另见here

答案 3 :(得分:2)

如何生成随机和唯一ID是一个有用的问题 - 但您似乎在生成生成它们的时做了反生产的假设!

我的观点是,您在创建行时不需要生成这些ID,因为它们基本上与所插入的数据无关。

我所做的是预先生成随机ID以供将来使用,这样我就可以享受自己的甜蜜时光并绝对保证它们是独一无二的,并且在插入时无需进行任何处理。

例如,我有一个带有order_id的订单表。当用户输入订单时,会立即生成此ID,永久递增1,2,3等。用户无需查看此内部ID。

然后我有另一个表 - random_ids(order_id,random_id)。我有一个每天晚上运行的例程,它预先加载了足够多的行,以覆盖可能在接下来的24小时内插入的订单。 (如果我在一天内获得10000份订单,​​我就会遇到问题 - 但这将是一个很好的问题!)

这种方法保证了唯一性,并且可以将任何处理负载从插入事务转移到批处理例程中,而不会影响用户。

答案 4 :(得分:0)

你最好的选择可能是某种形式的哈希函数,然后将校验和添加到最后。

答案 5 :(得分:0)

如果你不经常使用这个(你每秒都没有新客户,是吗?)那么只需获得一个随机数然后尝试插入记录就可行了。只要准备好在其失败时使用另一个数字重试插入,并且存在唯一约束违规。

我使用数字1000000到999999(相同长度的900000个可能的数字)并使用UPC or ISBN 10 algorithm检查数字。 2个校验位数会更好,因为它们可以消除99%的人为错误,而不是9%。