问题
I have a column that I would like to default to a randomly generated int8 in a specified range. I would also like this column to be unique, so if a random value is generated that already exists, it should be rerolled.
So my question is what the most idiomatic way to do the above is in PostgreSQL, ideally with good performance and supporting bulk inserts.
For example if I had a Person table with columns name and id, and I want id to be a random unique int8 in the range (0, 999999). I would want to be able to insert Paul, Kelly, David and Katie and get something like the following:
| Name | id |
+-------+--------+
| Paul | 314563 |
| Kelly | 592103 |
| David | 127318 |
| Katie | 893134 |
With no risk of duplicates and no risk of an insertion failure.
The range is not going to be large enough for me to safely assume they will never collide (i.e Birthday Paradox).
I should also say I do want true unpredictable randomness, so a cipher on a sequence would not count.
There are a variety of answers on how to generate random numbers, so the main focus of the question is the uniqueness aspect.
With that said a clean and efficient way to generate an int8 uniformly in an arbitrarily large range would be appreciated. random() * n starts having gaps when n > 2 ^ 53 (perhaps earlier).
回答1:
A possible solution :
create table t (name varchar (50), id int);
with cte as
(
SELECT a.n as possible_id
from generate_series(1, 150000) as a(n)
where not exists (select 1 from t where t.id = a.n)
)
, cte_s as
(
select (string_to_array(string_agg(cast(possible_id as varchar), ','), ','))[floor(random() * 150000 + 1)] as new_id
from cte
)
insert into t
values ('test', (select new_id from cte_s)::int);
select *
from t;
The fiddle : http://sqlfiddle.com/#!17/17d42/23
You can modify the maximum amount as you want of course.
来源:https://stackoverflow.com/questions/59409519/random-default-column-value-that-rerolls-on-conflict