Sql “插入到t(x)…选择最大值(t.x)+1..”有时返回“重复键”

Sql “插入到t(x)…选择最大值(t.x)+1..”有时返回“重复键”,sql,postgresql,sql-insert,Sql,Postgresql,Sql Insert,晚上好, 我对一个简单的INSERT sql语句有一个问题,我假设它是炸弹安全的,但有时我会遇到复制密钥错误 好吧,我会给你一个简单的sql代码 -说明1:表格创建 -说明2:插入件 我面临的问题是,有时没有任何原因,指令2会返回字段代码插入的重复键。 显然是随机发生的,我无法解释为什么会发生,也无法解释我如何在办公桌上重现这个问题。 唯一有效的解决方案似乎是捕获错误并重试插入,直到插入正常为止 好的,解决方案已经提供,但主要问题是,这种使用insert-into的方法在一个有很多表的大型应用程

晚上好,

我对一个简单的INSERT sql语句有一个问题,我假设它是炸弹安全的,但有时我会遇到复制密钥错误

好吧,我会给你一个简单的sql代码

-说明1:表格创建

-说明2:插入件

我面临的问题是,有时没有任何原因,指令2会返回字段代码插入的重复键。 显然是随机发生的,我无法解释为什么会发生,也无法解释我如何在办公桌上重现这个问题。 唯一有效的解决方案似乎是捕获错误并重试插入,直到插入正常为止

好的,解决方案已经提供,但主要问题是,这种使用insert-into的方法在一个有很多表的大型应用程序中被广泛使用,我希望避免重新排列所有代码,在这些代码中,为不同的表调用相同的操作

感谢您提供的帮助

改用类似于自动增量的方法

CREATE TABLE table_name(
    id SERIAL
);
永远不要使用COUNT+1,因为两个用户在同一时间可能会获得相同的ID,如果您进一步使用该ID,则可能会混淆ID

为了更好的顺序:在像java这样的编程语言中,插入后不带ID,您可以使用getGeneratedKeys来获取生成的主键。

使用,这类似于AUTOINCREMENT

CREATE TABLE table_name(
    id SERIAL
);
永远不要使用COUNT+1,因为两个用户在同一时间可能会获得相同的ID,如果您进一步使用该ID,则可能会混淆ID


为了更好的顺序:在像java这样的编程语言中,插入后不带ID,您可以使用getGeneratedKeys来获取生成的主键。

您的代码受到竞争条件的约束。运行该代码的两个并行进程都将读取相同的最大值,但其中只有一个进程将成功输入递增的值。换句话说,操作过程不是原子的

为了实现真正的原子化方式,如果您不介意间隙,那么可以使用序列,这是最好的解决方案,或者使用一个单独的表和一个单独的条目,以原子方式递增

UPDATE counter_table
SET c = c + 1
RETURNING c;
另一种方法是使用可序列化事务隔离级别,但这并不比您的代码好多少


您也可以简单地使用代码,但在插入之前设置一个保存点。如果出现错误,请回滚到保存点,获取下一个合适的数字,然后重试。

您的代码受到竞争条件的约束。运行该代码的两个并行进程都将读取相同的最大值,但其中只有一个进程将成功输入递增的值。换句话说,操作过程不是原子的

为了实现真正的原子化方式,如果您不介意间隙,那么可以使用序列,这是最好的解决方案,或者使用一个单独的表和一个单独的条目,以原子方式递增

UPDATE counter_table
SET c = c + 1
RETURNING c;
另一种方法是使用可序列化事务隔离级别,但这并不比您的代码好多少


您也可以简单地使用代码,但在插入之前设置一个保存点。如果出现错误,请回滚到保存点,获取下一个合适的数字,然后重试。

这可能发生在并发的不可序列化事务中。想象一下,两个事务获得相同的MAXCODE,具有相同的原始数据视图,并尝试插入相同的值。。也就是说,问题不是任意插入;相反,在没有适当锁升级的情况下可能导致违反约束的特定插入请参见事务隔离级别。对于特定的用例,插入重试可能是合适的,具体取决于表访问和所需的语义。或者,如果代码不需要是密集序列,则串行列类型a PgSQL功能可能很有用-在大型应用程序中广泛使用,则会有大量错误。这种方法不仅是绝对不正确的,而且也是效率最低的。您必须咬紧牙关,修复代码,才能使用正确的基于序列的ID生成。@user2864740与JoopEggenIt的注释相同,在并发的不可序列化事务中可能出现。想象一下,两个事务获得相同的MAXCODE,具有相同的原始数据视图,并尝试插入相同的值。。也就是说,问题不是任意插入;相反,在没有适当锁升级的情况下可能导致违反约束的特定插入请参见事务隔离级别。对于特定的用例,插入重试可能是合适的,具体取决于表访问和所需的语义。或者,如果代码不需要是密集序列,则串行列类型a PgSQL功能可能很有用-在大型应用程序中广泛使用,则会有大量错误。这种方法不仅是绝对不正确的,而且也是效率最低的。您必须咬紧牙关,修复代码,才能使用正确的基于序列的ID生成。@user2864740与JoopEg的注释相同
Gen对于现代Postgres版本,建议使用标识栏:@a_horse_和_no_name。如果你能回答这个问题,我会很高兴,这样我就可以删除我的。请随意复制文本。无需获取点数。在java等编程语言中,插入后无需ID,您可以使用getGeneratedKeys获取生成的主键-在任何支持DB连接的编程语言中,您只需使用insert/update。。。在java JDBC中返回*@Abelisto one call executeUpdate,并带有insert/update的额外标志,该标志仅返回int update计数。然后可以调用PreparedStatement.getGeneratedKeys。这是有点间接的;因此提及。Ĝis revido.@AttilioDM:串行接口没有功能问题。标识列的优点是,最终很难忘记正确使用它们。但是使用序列、序列列和标识列都在使用的序列是生成唯一值的唯一可伸缩且正确的方法。现代Postgres版本建议使用标识列:@a_horse_和_no_name如果你能将其作为答案,我会很高兴,所以我可以删除我的。请随意复制文本。无需获取点数。在java等编程语言中,插入后无需ID,您可以使用getGeneratedKeys获取生成的主键-在任何支持DB连接的编程语言中,您只需使用insert/update。。。在java JDBC中返回*@Abelisto one call executeUpdate,并带有insert/update的额外标志,该标志仅返回int update计数。然后可以调用PreparedStatement.getGeneratedKeys。这是有点间接的;因此提及。Ĝis revido.@AttilioDM:串行接口没有功能问题。标识列的优点是,最终很难忘记正确使用它们。但是,使用序列、序列和标识列都使用的序列是生成唯一值的唯一可伸缩且正确的方法。实际上,我们正在尝试计算这种插入记录的方法使用了多少次。我们可能会将列的数据类型传递给SERIAL,并编辑每一条与编写的post类似的SQL语句。无论如何,是否有办法强制序列化每个sql语句事务?您不能轻松地强制序列化,但您可以轻松地拥有可序列化的事务,这在逻辑上是等效的,但具有更好的并发性。是的,听起来一个简单的序列是你最好的选择。事实上,我们正在试图弄清楚这种插入记录的方法被使用了多少次。我们可能会将列的数据类型传递给SERIAL,并编辑每一条与编写的post类似的SQL语句。无论如何,是否有办法强制序列化每个sql语句事务?您不能轻松地强制序列化,但您可以轻松地拥有可序列化的事务,这在逻辑上是等效的,但具有更好的并发性。是的,听起来简单的序列是您的最佳选择。