说我有一个表像帖子,其中有像id,正文,created_at典型的列。我想创建一个独特的字符串与每个职位的创建,用于像一个URL缩短之类的东西。所以也许是一个10个字符的字母数字字符串。它必须在表格中是唯一的,就像主键一样。如何在Postgres的表中为每条记录生成唯一的字符串?
理想的情况下会有一种方式,Postgres而言,要处理这两个问题:
- 生成的字符串是
- 确保其唯一性
而且他们必须去手牵手,因为我的目标是不必担心应用程序中的任何唯一性强制代码。
说我有一个表像帖子,其中有像id,正文,created_at典型的列。我想创建一个独特的字符串与每个职位的创建,用于像一个URL缩短之类的东西。所以也许是一个10个字符的字母数字字符串。它必须在表格中是唯一的,就像主键一样。如何在Postgres的表中为每条记录生成唯一的字符串?
理想的情况下会有一种方式,Postgres而言,要处理这两个问题:
而且他们必须去手牵手,因为我的目标是不必担心应用程序中的任何唯一性强制代码。
看看布鲁斯的博客。这让你分开。你将不得不确保它不存在。也许concat主键?
Generating Random Data Via Sql
“以往需要生成随机的数据吗?您可以轻松地做到这一点在客户端应用程序和服务器端的功能,但它有可能在SQL生成随机数据。下面的查询产生的40五行字符数限制长度的小写字母字符串:”
SELECT
(
SELECT string_agg(x, '')
FROM (
SELECT chr(ascii('a') + floor(random() * 26)::integer)
FROM generate_series(1, 40 + b * 0)
) AS y(x)
)
FROM generate_series(1,5) as a(b);
唯一性执法是我更感兴趣的 - 这很容易对于我来说,要生成一个字符串,但是从应用程序方面来强制执行程序的执行是很痛苦的。我已经更新了我的问题,以更清楚地解释这一点。 –
从逻辑上讲,有两种方法。检查所有现有记录。或者为你的字符串引入某种有保证的独特性。我的想法是将主键连接到随机字符串。 – Kuberchaun
我不主张以下是有效率的,但它是我们如何在过去所做的这样的事情。
CREATE FUNCTION make_uid() RETURNS text AS $$
DECLARE
new_uid text;
done bool;
BEGIN
done := false;
WHILE NOT done LOOP
new_uid := md5(''||now()::text||random()::text);
done := NOT exists(SELECT 1 FROM my_table WHERE uid=new_uid);
END LOOP;
RETURN new_uid;
END;
$$ LANGUAGE PLPGSQL VOLATILE;
make_uid()
可以用作默认为在my_table
一列。例如:
ALTER TABLE my_table ADD COLUMN uid text NOT NULL DEFAULT make_uid();
md5(''||now()::text||random()::text)
可以根据口味调整。你可以考虑encode(...,'base64')
,除了在base-64中使用的一些字符不是URL友好的。
这可能导致竞争状态吗? (我对pg函数的运行时环境不够了解,以至于无法想象......) –
在数据中使用主键。如果您确实需要字母数字唯一字符串,则可以使用base-36编码。在PostgreSQL中,您可以使用this函数。
实施例:
select base36_encode(generate_series(1000000000,1000000010));
GJDGXS
GJDGXT
GJDGXU
GJDGXV
GJDGXW
GJDGXX
GJDGXY
GJDGXZ
GJDGY0
GJDGY1
GJDGY2
使用的Feistel网络。这种技术可以有效地在不产生任何碰撞的情况下在固定时间内生成唯一的随机查找字符串
对于包含6个字母的约20亿个可能字符串(2^31
)的版本,请参见this answer。
对于基于bigint
(9223372036854775808
不同可能值)一个63个比特版本,请参阅this other answer。
您可以更改回合函数,如第一个答案中所介绍的,以引入一个秘密元素以拥有您自己的一系列字符串(不可猜测)。
最简单的方法可能是使用序列来保证唯一性 (所以序列后添加修复X位随机数):
CREATE SEQUENCE test_seq;
CREATE TABLE test_table (
id bigint NOT NULL DEFAULT (nextval('test_seq')::text || (LPAD(floor(random()*100000000)::text, 8, '0')))::bigint,
txt TEXT
);
insert into test_table (txt) values ('1');
insert into test_table (txt) values ('2');
select id, txt from test_table;
但是这样会浪费巨大的记录量。 (注意:如果你最后使用8位数的随机数,最大bigInt是9223372036854775807,你只能有922337203条记录,你可能不需要8位数字,也可以查看你的编程环境的最大数量!)
或者你可以使用varchar作为id,甚至可以使用to_hex()转换上面的数字,或者像下面一样更改为base36(但是对于base36,尽量不要将其暴露给客户,以避免出现一些有趣的字符串显示!):
PostgreSQL: Is there a function that will convert a base-10 int into a base-36 string?
“浪费了大量的记录” - 你是什么意思? –
补充说明一下 – holdfenytolvaj
你不能只使用主键吗? –
我想拥有一个不向公众公开对象数量的面向外部的标识符。 –
我找到了两个解决我的问题的项目:https://github.com/inscitiv/pg_random_id https://github.com/norman/friendly_id –