IntegrityError:重复的键值违反了唯一约束“ myapp_mymodel_pkey”
详细信息:键(id) =(1)已经存在。
我的Postgres数据库实际上有一个myapp_mymodel对象,其主键为1。
为什么Postgres尝试使用那个主键又来了?或者,这很可能是我的应用程序(或Django的ORM)导致此问题的原因?
此问题现在连续发生了3次以上。我发现的是,对于给定的表,当它确实发生时,它会连续发生一次或多次,然后不再发生。它似乎在每个表完全停止运行几天之前就发生了,每个表至少发生一分钟左右才发生,并且只是间歇性发生(并非所有表都立即发生)。
这个错误是如此断断续续的事实(在2周内仅发生了3次左右-数据库上没有其他负载,只是我测试了我的应用程序)使我对低级问题非常警惕。
#1 楼
PostgreSQL不会尝试自己插入重复的值,而是您(您的应用程序,包括ORM)来执行。它可以是将值输入到设置在错误位置的PK以及表中已经包含等于其
nextval()
的值的序列-或仅仅是您的应用程序执行了错误的操作。第一个易于修复:SELECT setval('your_sequence_name', (SELECT max(id) FROM your_table));
第二个意味着调试。
Django(或任何其他流行的框架)不自行重置序列-否则我们每隔一天就会遇到类似的问题。
评论
值得注意(也基于此处的@andi回答)不同的隔离级别吗?例如,如果第二个查询在第一个查询完成之前进入,在我不使用事务的情况下,是否有可能在第一个查询完成之前插入一条导致获取max(id)的记录,然后导致两者具有相同的结果?
–orokusaki
16年11月15日在16:54
#2 楼
您很可能会在未更新其串行列序列值的表中插入一行。请考虑表中的下一列,这是Django ORM为postgres定义的主键。 >
id serial NOT NULL
其默认值设置为
nextval('table_name_id_seq'::regclass)
仅当id字段设置为空白时才评估序列。但是如果表中已经有条目,那就是问题。
问题是为什么那些较早的条目不触发序列更新?这是因为id值是为所有较早的条目显式提供的。
在我的情况下,那些初始条目是通过迁移从Fixture加载的。
也可以通过具有随机PK值的自定义条目来解决此问题。
例如说您的表中有10个条目。您使用PK = 15进行显式输入。接下来的四个通过代码插入将完全正常,但第5个插入将引发异常。
DETAIL: Key (id)=(15) already exists.
评论
感谢您对这篇文章。我已经调试了很长时间这样的情况。很少发生。事实证明,特定的“手动”管理功能可以自行插入ID,而使身份计数器具有旧值。这是“默认身份产生”的真正危险。在下次定义标识列时,在使用“按默认”而不是“总是”之前,我会三思。
–迈克尔
19年11月21日在15:00
#3 楼
我最终在这里遇到了同样的错误,这种错误很少发生,并且很难跟踪,因为我一直在寻找它,而不是我应该去的地方。错误是JS重复,它正在对POST执行服务器两次!
所以有时候值得一看,不仅是在django(或任何其他Web框架)的视图和表单上,而且还有在正面发生的事情。
#4 楼
是的,很奇怪。就我而言,在迁移过程中加载数据时显然出现了问题。我添加了空迁移,并在行中添加了一些初始数据,以我的情况为例,记录了6条记录。db_alias = schema_editor.connection.alias
bulk = []
for item in items:
bulk.append(MyModel(
id=item[0],
value=item[1],
slug=item[2],
name=item[3],
))
MyModel.objects.using(db_alias).bulk_create(bulk)
然后在管理面板中,我尝试添加新项目并得到:
第一次尝试:
DETAIL: Key (id)=(1) already exists.
以后的尝试:
DETAIL: Key (id)=(2) already exists.
DETAIL: Key (id)=(3) already exists.
DETAIL: Key (id)=(4) already exists.
DETAIL: Key (id)=(5) already exists.
DETAIL: Key (id)=(6) already exists.
最后第7次按时都成功了
所以我说可能是与bulk_create相关的东西,因为我在那里加载了6个项目。可能是您的Django项目中的原因类似。
Django 1.9
PostgreSQL 9.3.14
评论
当我使用django模型的常规.create()方法时,出现了相同的错误。我已经将数据手动导入到postgre数据库的表中,这就是为什么导致此错误的原因。
– SukanyaPai
20/12/29在18:34
#5 楼
您也可以执行以下操作:ALTER SEQUENCE 'sequence_name_goes_here' name" RESTART WITH (SELECT max(id) + 1 FROM 'table_name_goes_here');
评论
Django特别指出,除非指定,否则主键是由DBMS生成的-现在,我不知道@orokusaky在他的python代码中正在做什么,但是我最终在此页面上是因为我非常有信心自己没有代码尝试使用特定的主键,而我从未见过DBMS尝试使用错误的主键。