sql – 如何在Postgres 9.6中生成长度为N的随机,唯一,字母数字ID?

我在StackOverflow上看到了一堆different solutions,它跨越了很多年和许多Postgres版本,但是有了一些像gen_random_bytes这样的新功能,我想再问一下,看看在新版本中是否有更简单的解决方案.

给定包含a-zA-Z0-9的ID,并根据它们的使用位置而变化,例如……

bTFTxFDPPq
tcgHAdW3BD
IIo11r9J0D
FUW5I8iCiS

uXolWvg49Co5EfCo
LOscuAZu37yV84Sa
YyrbwLTRDb01TmyE
HoQk3a6atGWRMCSA

HwHSZgGRStDMwnNXHk3FmLDEbWAHE1Q9
qgpDcrNSMg87ngwcXTaZ9iImoUmXhSAv
RVZjqdKvtoafLi1O5HlvlpJoKzGeKJYS
3Rls4DjWxJaLfIJyXIEpcjWuh51aHHtK

(像IDs that Stripe uses.)

在Postgres 9.6中,如何通过一种简单的方法为不同的用例指定不同的长度,如何随机安全地生成它们(减少冲突并降低可预测性)?

我认为理想情况下解决方案的签名类似于:

generate_uid(size integer) returns text

根据您自己的权衡,可以根据自己的权衡来定制尺寸,以降低碰撞的可能性,减少可用性的字符串大小.

据我所知,它必须使用gen_random_bytes()而不是random()来获得真正的随机性,以减少猜测它们的可能性.

谢谢!

我知道UUID有gen_random_uuid(),但在这种情况下我不想使用它们.我正在寻找能给我ID类似于Stripe(或其他人)使用的ID的东西,它们看起来像:“id”:“ch_19iRv22eZvKYlo2CAxkjuHxZ”尽可能短,但仍然只包含字母数字字符.

这个要求也是为什么编码(gen_random_bytes(),’hex’)不适合这种情况,因为它减少了字符集,因此迫使我增加字符串的长度以避免冲突.

我目前正在应用程序层中执行此操作,但我希望将其移动到数据库层以减少相互依赖性.以下是在应用程序层中执行此操作的Node.js代码可能如下所示:

var crypto = require('crypto');
var set = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

function generate(length) {
  var bytes = crypto.randomBytes(length);
  var chars = [];

  for (var i = 0; i < bytes.length; i++) {
    chars.push(set[bytes[i] % set.length]);
  }

  return chars.join('');
}
最佳答案
评论,

[a-z]中> 26个字符
[A-Z]中> 26个字符
[0-9]中> 10个字符
> [a-zA-Z0-9]中的62个字符(base62)
>功能substring(string [from int] [for int])看起来很有用.

所以它看起来像这样.首先,我们证明我们可以采用随机范围并从中拉出.

SELECT substring(
  'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
  1, -- 1 is 'a', 62 is '9'
  1,
);

现在我们需要1到63之间的范围

SELECT trunc(random()*62+1)::int+1
FROM generate_series(1,1e2) AS gs(x)

这让我们在那里..现在我们只需要加入这两个..

SELECT substring(
  'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
  trunc(random()*62)::int+1
  1
)
FROM generate_series(1,1e2) AS gs(x);

然后我们将其包装在ARRAY constructor (because this is fast)

SELECT ARRAY(
  SELECT substring(
    'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
    trunc(random()*62)::int+1,
    1
  )
  FROM generate_series(1,1e2) AS gs(x)
);

并且,我们调用array_to_string()来获取文本.

SELECT array_to_string(
  ARRAY(
      SELECT substring(
        'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
        trunc(random()*62)::int+1,
        1
      )
      FROM generate_series(1,1e2) AS gs(x)
  )
  , ''
);

从这里我们甚至可以把它变成一个功能..

CREATE FUNCTION random_string(randomLength int)
RETURNS text AS $$
SELECT array_to_string(
  ARRAY(
      SELECT substring(
        'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
        trunc(random()*62)::int+1,
        1
      )
      FROM generate_series(1,randomLength) AS gs(x)
  )
  , ''
)
$$LANGUAGE SQL
RETURNS NULL ON NULL INPUT
VOLATILE LEAKPROOF;

然后

SELECT * FROM random_string(10);

转载注明原文:sql – 如何在Postgres 9.6中生成长度为N的随机,唯一,字母数字ID? - 代码日志