1

我有这样的表:

class Tag(Base):
    __tablename__ = 'tag'

    id = Column(Integer, primary_key=True)
    name = Column(Unicode(50), nullable=False)

    def __init__(self, name):
        self.name = name

假设表是空的。比我以这种方式插入一些数据:

t = Tag('first tag')
t.id = 2
dbsession.add(t)
dbsession.commit()

没关系:我的 postgresql 表中有一行 id = 2。下一步:

t = Tag('second tag')
dbsession.add(t)
dbsession.commit()

比我有 2 行: {id: 1, name: 'second tag'} 和 {id: 2, name: 'first_tag'}

下一步(最后一步):

t = Tag('third tag')
dbsession.add(t)
dbsession.commit()

IntegrityError:(IntegrityError)重复键值违反唯一约束“tag_pkey”

(它想创建 id = 2 的行,但它已经使用第一个标签)

是否有可能以某种方式对 postgres 说:如果存在这样的 pkey,请尝试下一步直到它可用?

在使用转储时非常有用。

提前致谢!

4

1 回答 1

4

我不知道有任何安全、有效的方式来做你想做的事。您真的应该选择是要自己定义密钥还是使用生成的密钥并坚持一种策略或另一种策略。

如果你不介意可怕的并发,你可以LOCK TABLE thetable做你的工作,setval表的标识符序列到你插入后的下一个空闲值,并commit释放锁。但是,这仍然会导致显式调用nextval(如许多 ORM)的应用程序出现问题,而不是让数据库通过从?INSERT列列表中省略它来定义值或将其显式命名为DEFAULT.

否则,您可以让您的代码(或 PL/PgSQL 辅助函数)在重试循环中执行插入操作,该循环会增加密钥并在遇到完整性错误时再次尝试。如果您需要做的不仅仅是每个事务的一次插入,则此策略将不起作用。此外,在SERIALIZABLE隔离模式下,我认为您不能使用 PL/PgSQL 来做到这一点,您必须使用客户端重试循环来处理序列化失败。

这是一个可怕的想法。要么一致地使用应用程序定义的键,要么一致地使用数据库定义的键。不要将两者混为一谈。

于 2012-09-10T07:23:18.380 回答