如何确保零顺序的唯一数字字段

时间:2014-09-03 08:01:20

标签: postgresql plpgsql

这是一个包含字段id,id_user,order_id的表。 创建记录以查找最后一个用户数并按顺序插入以下内容时需要。 我写了一个存储过程,它将下一个订单号带给用户,但即使它没有提供唯一的订单号。

CREATE OR REPLACE FUNCTION get_next_order()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $function$
DECLARE
  next_order_num bigint;
BEGIN
  select order_id + 1 INTO next_order_num
    from payment_out
   where payment_out.id_usr = NEW.id_usr
     and payment_out.order_id is not null
order by payment_out.order_id desc
   limit 1;

--  if payments does't exist, return 1
  NEW.order_id = coalesce(next_order_num, 1);
  return NEW;
END;
$function$

CREATE TRIGGER get_next_order 
 BEFORE INSERT 
     ON payment_out 
FOR EACH ROW EXECUTE 
PROCEDURE get_next_order()

如何避免重复的订单号?

1 个答案:

答案 0 :(得分:0)

为了在存在多个并发事务的情况下工作,为同一个用户插入命令,您需要锁定特定记录以使它们等待并以串行方式执行。

例如,在第一个SELECT之前,您可以:

PERFORM 1 FROM "users" where id_user = NEW.id_user FOR UPDATE;

您锁定拥有订单的父“用户”记录。

否则,多个并发事务可以同时执行您的过程,但是他们无法看到彼此的插入值,因此他们将选择相同的数字。

但是,请注意:当您插入依赖于它的表时,外键约束将导致SHARE条目已经被users锁定。您的触发器会尝试将其升级为UPDATE锁定,但多个事务可能已经锁定SHARE,因此会阻止。你将找到所有等待彼此的事务,直到PostgreSQL在死锁中止错误中杀死其中一个而不是其中一个。避免这种情况的唯一方法是应用程序到<{1}} 之前它会为该用户创建订单。

变体是在SELECT 1 FROM users WHERE id_user = blahblah FOR UPDATE中保留next_order_id字段并执行users,并使用其结果来设置订单ID。同样的锁定升级问题也适用。