Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Postgresql Postgres函数创建唯一字符串并插入到表中_Postgresql_Function_Loops_Unique Constraint - Fatal编程技术网

Postgresql Postgres函数创建唯一字符串并插入到表中

Postgresql Postgres函数创建唯一字符串并插入到表中,postgresql,function,loops,unique-constraint,Postgresql,Function,Loops,Unique Constraint,我在Postgres中有一个表,其中包含用户id和外部id的列,这些列将用于外部服务的API调用。在调用外部API之前,我必须在我这边创建外部id,验证其唯一性,并将其存储在PG中。 这篇文章非常接近我想要的: 但是,如果两个并发调用生成相同的id,则可能会发生冲突。我想做的是创建一个生成随机字符串的循环,然后尝试使用用户id将其插入表中。如果随机字符串已经存在(列上有唯一的约束),则它应该失败。如果失败,它应该生成另一个id并尝试插入该id(一旦我得到工作代码,我将添加一个计数器,以防止对db

我在Postgres中有一个表,其中包含用户id和外部id的列,这些列将用于外部服务的API调用。在调用外部API之前,我必须在我这边创建外部id,验证其唯一性,并将其存储在PG中。 这篇文章非常接近我想要的: 但是,如果两个并发调用生成相同的id,则可能会发生冲突。我想做的是创建一个生成随机字符串的循环,然后尝试使用用户id将其插入表中。如果随机字符串已经存在(列上有唯一的约束),则它应该失败。如果失败,它应该生成另一个id并尝试插入该id(一旦我得到工作代码,我将添加一个计数器,以防止对db造成冲击)

你会怎么写这个循环?如果插入返回错误(约束检查),则循环应继续,否则将再次循环。我已经检查了Postgres文档,但似乎找不到(或我找不到)检查查询错误代码/状态的方法

更新

我想出了一个可能的解决办法,但需要充实一下。以下是pidgeon sql中的内容,让我思考一下这个问题:

success = true;
LOOP
-- create random string function
BEGIN
  insert string
EXCEPTION
  success = false;
EXIT WHEN success;
END;

如果不需要外部ID的随机性,则

创建序列基\u seq;
改变表格
ALTER列ext_id设置默认LPAD(nextval('base_seq')::text,64,'0');
将在ext_id列中提供强唯一(数据库范围)字符串

但是,如果您唯一的选择是在循环中尝试,那么plpgsql函数中的循环将如下所示:

循环
new\u try\u ext\u id:=这里有一些随机魔术。。。
插入表格(用户id、外部id)
值(someid、new\u try\u ext\u id)
在冲突上无所作为;
获取一些整数变量=行计数;
当某个整数变量>0时退出;
端环;
修订版: 您对使用序列的安全性担心可能有一定的合理性,尽管我不记得在安全审计中也会出现这种情况。但是,如果这是一项业务需求,那么您必须遵循它。在我看来,您需要处理多个表的键冲突,因此广义函数广义生成似乎适合每个表使用特定的插入函数。您需要为每个表编写insert函数,不能仅使用insert语句,必须使用函数(或过程,如果您使用的是Postgres V12或更高版本)。您还必须将每列作为参数传递给insert函数。以下内容基本上“充实”了您的伪代码

create or replace function generate_random_id
                    ( lower_value_in bigint default 1  
                    , upper_value_in bigint default 10000000000)
                                                     
   returns bigint
  language sql
  volatile strict 
as $$
    select floor(random()*(upper_value_in-lower_value_in+1)+1)::bigint ;
$$; 
 
create or replace function insert_atable(col_x_in atable.colx%type)
   returns void 
  language plpgsql 
as $$
declare
    l_invalid_id boolean := true;
begin 
    while l_invalid_id
    loop
       begin
           insert into atable( id, colx)
             values ( generate_random_id(),col_x_in); 
           l_invalid_id := false;
       exception 
          when unique_violation then null;         
       end;
    end loop;
end;
$$;   
修订

当然,你可以放弃这个想法,或者两个ID实际上是一样的

原件: 因此,面向外部的id必须是唯一的,但为什么是随机的呢。从序列生成id,然后将该序列的最大值限制为99999999。然后将生成的序列转换为文本并存储该结果。这样,内部id和外部id都是唯一的,但具有相同的值(至少当外部强制转换类型为id时)。更好的是,如果您的Postgres 12更高,您可以将外部id定义为id上生成的列,从而保证它们始终相同。表定义类似于:

create table atable
             ( id integer  generated always as identity (maxvalue 999999999)
             , ext_id text generated always as  (id::text) stored
             , colx text 
             ) ;

看。注:演示将id定义为“默认生成”。这仅用于演示目的。

扩展可以工作吗?不幸的是,外部API不使用UUIDYeah,而是:
选择uuid\u generate\u v1mc()::varchar;3c464166-fe95-11ea-87f2-0fd30065b715选择pg_类型(uuid_generate_v1mc()::varchar);字符变化
@JEPrice:然后将uuid转换为字符串它太长了。不知道为什么外部api开发人员会这样做,但它最多10个字符:/n这将用于面向外部的应用程序,因此序列不是我想使用的东西,它看起来比上面的pidgeon sql更好。我需要花一些时间,用一个随机字符串生成器将其封装在一个函数中,再添加一些其他内容,这可能就是我需要的…随机防止钓鱼。如果id是连续的,那么猜测一个id将非常容易,学习一个id将打开一系列id进行攻击。