IntegrityError重复键值违反唯一约束-django / postgres


70

我正在跟我刚才提出的一个问题进行跟进,在这个问题中,我寻求从愚蠢/编写不佳的mysql查询转换为postgresql。我相信我成功了。无论如何,我使用的是从mysql数据库手动移动到postgres数据库的数据。我正在使用如下查询:

  """
  UPDATE krypdos_coderound cru

  set is_correct = case 
      when t.kv_values1 = t.kv_values2 then True 
      else False 
      end

  from 

  (select cr.id, 
    array_agg(
    case when kv1.code_round_id = cr.id 
    then kv1.option_id 
    else null end 
    ) as kv_values1,

    array_agg(
    case when kv2.code_round_id = cr_m.id 
    then kv2.option_id 
    else null end 
    ) as kv_values2

    from krypdos_coderound cr
     join krypdos_value kv1 on kv1.code_round_id = cr.id
     join krypdos_coderound cr_m 
       on cr_m.object_id=cr.object_id 
       and cr_m.content_type_id =cr.content_type_id 
     join krypdos_value kv2 on kv2.code_round_id = cr_m.id

   WHERE
     cr.is_master= False
     AND cr_m.is_master= True 
     AND cr.object_id=%s 
     AND cr.content_type_id=%s 

   GROUP BY cr.id  
  ) t

where t.id = cru.id
    """ % ( self.object_id, self.content_type.id)
  )

我有理由相信这很好。但是,这导致了新问题。尝试提交时,我从django收到一条错误,指出:

IntegrityError at (some url): 
duplicate key value violates unique constraint "krypdos_value_pkey"

我已经看过这里发布的一些回复,但是我还没有找到解决我的问题的方法(尽管相关问题引起了一些有趣的阅读)。我在日志中看到了这一点,这很有趣,因为我从未明确调用insert- django必须处理它:

   STATEMENT:  INSERT INTO "krypdos_value" ("code_round_id", "variable_id", "option_id", "confidence", "freetext")
   VALUES (1105935, 11, 55, NULL, E'') 
   RETURNING "krypdos_value"."id"

但是,尝试运行该操作将导致重复键错误。实际的错误抛出在下面的代码中。

 # Delete current coding         CodeRound.objects.filter(object_id=o.id,content_type=object_type,is_master=True).delete()
  code_round = CodeRound(object_id=o.id,content_type=object_type,coded_by=request.user,comments=request.POST.get('_comments',None),is_master=True)
  code_round.save()
  for key in request.POST.keys():
    if key[0] != '_' or key != 'csrfmiddlewaretoken':
      options = request.POST.getlist(key)
      for option in options:
        Value(code_round=code_round,variable_id=key,option_id=option,confidence=request.POST.get('_confidence_'+key, None)).save()  #This is where it dies
  # Resave to set is_correct
  code_round.save()
  o.status = '3' 
  o.save(

我检查了序列等,它们似乎是有序的。在这一点上,我不确定该怎么做-我认为这是django的事情,但我不确定。任何反馈将不胜感激!


1
顺便说一句:根据De Morgan的法律,您的情况key[0] != '_' or key != 'csrfmiddlewaretoken'等同于not (key[0] == '_' and key == 'csrfmiddlewaretoken')。应该很容易看出内部条件从未满足,所以它等效于not (False)或换句话说True。但是,为什么还要打扰if呢?
JonasKölker2013年

python manage.py sqlsequencereset <app> | python manage.py dbshell
用户

这以前的答案给出了更多的细节和光的主题:stackoverflow.com/questions/244243/...
RedSands

Answers:


157

这发生在我身上-事实证明,您需要在Postgres中重新同步主键字段。关键是SQL语句:

SELECT setval('tablename_id_seq', (SELECT MAX(id) FROM tablename)+1);

就是这样!我以为我已经解决了这个问题,但是事实证明我重置了错误的值。
the_man_slim 2012年

1
我知道有很多人遇到过这个问题-很高兴您解决了这个问题!
Hacking Life

我正要问@HackingLife,您是否知道为什么会发生……对于我们来说,这是因为我们通过直接复制来同步来自不同数据库的所有数据(这是他的回答中提到的zaphod) 。当我们停止这样做并直接开始使用主数据库时,添加新模型时主键序列并未增加,从而产生此错误。
AJP

在通过SQL脚本手动导入内容后,我遇到了同样的问题,当我回到Flask的UI中添加新项时,它出现了问题。更新主键字段序列对我来说很成功。感谢您的修复!
约书亚鲍威尔

哇..我正在使用heroku,因此不得不将数据库移至Amazon AWS RDS。我在aws上导出并导入到新的postgres db,出现了此问题。确实如上所述将id序列重置到所有表,并且它像一个魅力一样工作。谢谢!
pavanw3b 2015年

29

后端和其他后端(如Postgres,Oracle等)(似乎不一样)似乎是MySQL和SQLite之间的行为差​​异(即使在插入具有显式id的对象时,它们也会更新下一个可用的主键)。 。

有一张票描述了同样的问题。即使它被关闭为无效,它也提示存在Django管理命令来更新下一个可用密钥。

要显示SQL更新应用程序MyApp的所有下一个ID :

python manage.py sqlsequencereset MyApp

为了执行该语句,可以将其提供为dbshel​​l管理命令的输入。对于bash,您可以输入:

python manage.py sqlsequencereset MyApp | python manage.py dbshell

管理命令的优点是抽象了基础数据库后端,因此即使以后迁移到其他后端也可以使用。


1
这挽救了我的一天
Keval

24

我的“清单”应用中已有一个表,我想在Django admin中添加新记录,但出现此错误:

重复的键值违反了唯一约束“ inventory_part_pkey”详细信息:键(part_id)=(1)已存在。

如前所述,我运行以下代码来获取用于重置ID-S的SQL命令:

python manage.py sqlsequencereset inventory

管道python manage.py sqlsequencereset inventory | python manage.py dbshell到外壳不起作用

  • 所以我复制了生成的原始SQL命令
  • 然后https://www.pgadmin.org为PostgreSQL打开pgAdmin3并打开我的数据库
  • 单击6.图标(执行任意SQL查询)
  • 复制语句生成了什么

在我的情况下,原始SQL命令为:

BEGIN;
SELECT setval(pg_get_serial_sequence('"inventory_signup"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "inventory_signup";
SELECT setval(pg_get_serial_sequence('"inventory_supplier"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "inventory_supplier";
COMMIT;

用F5执行。

这样就解决了所有问题。


3
这个答案挽救了我的一天!
Gambit2007年

2
这个答案应该是您实际需要的答案-这是最正确的方法!
德米特里·阿希普琴科

您是老板:)
Radek

9

除了zapphods回答:

在我的情况下,索引编制确实是不正确的,因为我已经删除了所有迁移,并且在开发时数据库大概是数据库的10至15倍,因为我还没有处于迁移任何阶段。

我在收到IntegrityError finished_product_template_finishedproduct_pkey

重新索引表并重新启动运行服务器:

我使用的是pgadmin3,无论哪个索引不正确,都会抛出重复的键错误,然后导航至constraints并重新索引。

在此处输入图片说明

然后重新索引。

在此处输入图片说明


重新索引对我不起作用,但是完全分析(也选中了冻结和分析选项)对我有用。我在桌上没有任何序列或触发器,但我认为一些未完成的插入卡在了某处,因此Vacuum Full有所帮助。
MrLehiste '18年

5

解决方案是,您需要按照“ Hacking Life”(编写示例SQL代码)报告的主键字段进行重新同步,但如“ Ad N”建议的那样,最好运行Django命令sqlsequencereset以获取可以复制并过去或使用其他命令运行。

作为对这些答案的进一步改进,我建议您和其他读者不要复制并粘贴SQL代码,但更安全地以sqlsequencereset这种方式(使用默认数据库)执行从python代码内部生成的SQL查询。:

from django.core.management.color import no_style
from django.db import connection

from myapps.models import MyModel1, MyModel2


sequence_sql = connection.ops.sequence_reset_sql(no_style(), [MyModel1, MyModel2])
with connection.cursor() as cursor:
    for sql in sequence_sql:
        cursor.execute(sql)

我使用Python3.6Django 2.0PostgreSQL 10测试了此代码。


不要对多个问题发表相同的答案。发表一个好答案,然后投票/标记以将其他问题重复作为一个重复。如果问题不是重复的,请定制对问题的答案
马丁·彼得斯


3

如果您想像我一样重置所有表上的PK,可以使用PostgreSQL建议的方式

SELECT 'SELECT SETVAL(' ||
       quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
       ', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
       quote_ident(PGT.schemaname)|| '.'||quote_ident(T.relname)|| ';'
FROM pg_class AS S,
     pg_depend AS D,
     pg_class AS T,
     pg_attribute AS C,
     pg_tables AS PGT
WHERE S.relkind = 'S'
    AND S.oid = D.objid
    AND D.refobjid = T.oid
    AND D.refobjid = C.attrelid
    AND D.refobjsubid = C.attnum
    AND T.relname = PGT.tablename
ORDER BY S.relname;

运行此查询后,您将需要执行查询结果。我通常将其复制并粘贴到记事本中。然后我查找和替换"SELECTSELECT;";。我复制并粘贴到pgAdmin III中并运行查询。它将重置数据库中的所有表。上面的链接中提供了更多的“专业”说明。


2

我遇到此错误是因为我以错误的方式将额外的参数传递给save方法。

对于遇到此问题的任何人,请尝试使用以下命令强制执行UPDATE:

instance_name.save(..., force_update=True)

如果遇到无法传递的错误,force_insert并且force_update可能同时传递错误的自定义参数,就像我一样。


这在Django中是一个不错的解决方案。从shell导入模型类(如果您未使用shell_plus),然后执行MyModelClass.objects.first()。save(force_update = True)
Doug Bradshaw

1

您只需要转到pgAdmin III,然后使用表名执行脚本:

SELECT setval('tablename_id_seq', (SELECT MAX(id) FROM tablename)+1);

0

我遇到了与OP相同的错误。

我创建了一些Django模型,基于这些模型创建了Postgres表,并通过Django Admin向Postgres表中添加了一些行。然后,我摆弄了模型中的某些列(更改ForeignKeys等),却忘记了迁移更改。

运行迁移命令解决了我的问题,鉴于上面的SQL回答,这很有意义。

要查看将要应用哪些更改而不实际应用它们:
python manage.py makemigrations --dry-run --verbosity 3

如果您对这些更改感到满意,请运行:
python manage.py makemigrations

然后运行:
python manage.py migrate


0

我遇到了类似的问题,似乎没有任何效果。如果需要数据(即在执行转储时不能排除数据),请确保已关闭(注释)了所有post_save接收器。我认为数据将被导入,但是由于这些原因,它将再次创建相同的模型。为我工作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.