随机的默认列值,在冲突时重做。[英] Random default column value that rerolls on conflict

本文是小编为大家收集整理的关于随机的默认列值,在冲突时重做。的处理方法,想解了随机的默认列值,在冲突时重做。的问题怎么解决?随机的默认列值,在冲突时重做。问题的解决办法?那么可以参考本文帮助大家快速定位并解决问题。

问题描述

我有一列我想 default 到指定范围内随机生成的 int8.我还希望此列是唯一的,因此如果生成的随机值已经存在,则应重新滚动.

所以我的问题是,在 PostgreSQL 中执行上述操作最惯用的方法是什么,最好具有良好的性能并支持批量插入.

例如,如果我有一个包含 name 和 id 列的 Person 表,并且我希望 id 是 (0, 999999) 范围内的随机唯一 int8.我希望能够插入 Paul、Kelly、David 和 Katie 并获得如下内容:

| Name  |   id   |
+-------+--------+
| Paul  | 314563 |
| Kelly | 592103 |
| David | 127318 |
| Katie | 893134 |

没有重复的风险,也没有插入失败的风险.

范围不会大到足以让我安全地假设它们永远不会发生碰撞(即生日悖论).

我还应该说我确实想要真正的不可预测的随机性,所以序列上的密码不会计算在内.

关于如何生成随机数有多种答案,所以问题的主要焦点是唯一性方面.

这样说,一种在任意大范围内均匀生成 int8 的干净有效的方法将不胜感激.random() * n 在 n > 2 ^ 53 时开始出现间隙(可能更早).

推荐答案

一个可能的解决方案:

create table t (name varchar (50), id int);

-- 1. generate a list of possible ids
-- 2. cast the id in varchar to make a string after that
-- 3. aggregate all the possible ids in a string with a ',' separator
-- 4. make the string a list
-- 5. select a random value in this list
-- 6. insert the new id for the wanted name. Here 'test'
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); 

-- test that your code doing what you want
select *
from t;

http://sqlfiddle.com/#!17/17d42/26

当然,您可以根据需要修改最大金额.

本文地址:https://www.itbaoku.cn/post/1764165.html