說我有一個表像帖子,其中有像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 –