如果在PostgreSQL中插入失败,有没有办法避免调用nextval()?

如果在PostgreSQL中插入失败,有没有办法避免调用nextval()?,sql,postgresql,Sql,Postgresql,在PostgreSQL数据库中,我有一个带有主键的表和另一个需要唯一的字段 CREATE TABLE users ( id INTEGER PRIMARY KEY DEFAULT nextval('groups_id_seq'::regclass), name VARCHAR(255) UNIQUE NOT NULL ); INSERT users (name) VALUES ('foo'); INSERT users (name) VALUES

在PostgreSQL数据库中,我有一个带有主键的表和另一个需要唯一的字段

  CREATE TABLE users (
    id      INTEGER PRIMARY KEY DEFAULT nextval('groups_id_seq'::regclass),
    name    VARCHAR(255) UNIQUE NOT NULL
  );

  INSERT users (name) VALUES ('foo');
  INSERT users (name) VALUES ('foo');
  INSERT users (name) VALUES ('bar');
第二次插入失败,但序列组_id _seq已递增,因此当添加“bar”时,会在id号中留下一个间隙


是否有一种方法可以告诉PostgreSQL仅在满足其他约束的情况下获取下一个值,或者如果名称不重复,我是否应该首先使用SELECT进行检查?这仍然不能保证没有间隙,但至少它会将间隙的数量减少到罕见的情况,即有另一个进程试图同时插入相同的名称

我不这么认为:序列的一个基本特征是间隙是可能的,可以想象两个并发事务,其中一个执行回滚。你应该忽略间隙。为什么它们在您的案例中是一个问题?

如果您需要无间隙序列,有很多方法可以做到这一点,但这并不简单,而且肯定要慢得多


另外,如果您担心使用太多id,只需将id定义为bigserial。

这样做是可能的,尽管很麻烦。同样,依赖于连续序列中的值是危险的,所以如果可以的话,最好保持原样

如果你不能:

对表的每次访问都可能导致一行具有特定的名称,即对该表的每次插入,如果您允许它(尽管这是一种糟糕的做法),那么每次可能更改名称字段的更新都必须在首先锁定soemthing的事务中执行。最简单也是性能最低的选项是在独占模式下使用lock users简单地锁定整个表,添加最后3个字允许其他进程并发读取访问,这是安全的

然而,这是一个非常粗糙的锁,如果有许多并发修改给用户,它会降低性能;更好的选择是锁定另一个表中必须已经存在的单个对应行。可以使用“选择…”锁定此行。。。更新。这只有在处理对另一个父表具有FK依赖关系的子表时才有意义

例如,想象一下,目前我们实际上正在尝试为客户安全地创建新订单,并且这些订单以某种方式具有可识别的“名称”。我知道,可怜的例子。。。订单对客户具有FK依赖性。然后,为了防止为给定客户创建两个同名订单,您可以执行以下操作:

BEGIN;

-- Customer 'jbloggs' must exist for this to work.  
SELECT 1 FROM customers
WHERE id = 'jbloggs'
FOR UPDATE

-- Provided every attempt to create an order performs the above step first,
-- at this point, we will have exclusive access to all orders for jbloggs.
SELECT 1 FROM orders
WHERE id = 'jbloggs'
AND order_name = 'foo'

-- Determine if the preceding query returned a row or not.
-- If it did not:
INSERT orders (id, name) VALUES ('jbloggs', 'foo');

-- Regardless, end the transaction:
END;
请注意,仅用SELECT。。。对于更新-如果行不存在,多个并发进程可能同时报告该行不存在,然后尝试同时插入,导致事务失败,从而导致序列间隔


任何一种锁定方案都可以工作;重要的是,任何试图创建同名行的人都必须尝试锁定同一对象。

这不是一个真正的问题,我只是惊讶地发现,如果可以基本避免,为什么不投入精力学习如何操作?我知道我无法100%地避免它。不,他们无法避免。还有其他一些情况也可能导致序列中存在相当大的间隙——只要围绕以下事实来设计应用程序,即序列保证不断递增,直到达到数据类型的最大值(32或64位),但不能保证是无间隙的。@Magnus:可以避免,但复杂性方面的代价很大,请参见我的答案。我完全同意,如果可能的话,最好是设计允许间隙。另请参见:可能的重复