繁体   English   中英

为什么需要运行postgresql nextval函数? 以及如何预防呢?

[英]Why do I need to run the postgresql nextval function? And how to prevent it?

我只是遇到了我不了解的Django和PostgreSQL问题。

我有一个简单的模型,定义如下:

class MyModel(models.Model):
    my_field = models.IntegerField()
    my_other_field = models.TextField()

在我看来,我有类似的东西:

my_object = MyModel(my_field=1, my_other_field='blah')
my_object.save()

直到今天早上,一切都很好。 我收到此错误:

 IntegrityError at /my_url/

duplicate key value violates unique constraint "my_model_pkey"
DETAIL:  Key (id)=(3) already exists.
CONTEXT:  Remote SQL command: INSERT INTO public.my_model(id, my_field, my_other_field) VALUES ($1, $2, $3) RETURNING id

我曾经遇到这个错误,我知道它与PostgreSQL用id列同步与我的模型关联的顺序表的方式有关。 我有PostgreSQL中,直到运行此功能id返回比最大的值id

select nextval('my_model_id_seq'::regclass);

我的问题是:为什么首先发生这种情况? 以及将来如何预防呢?

顺便说一句,这是我将数据插入表中的唯一方法,我从未手动插入数据。

我希望这个问题足够清楚

我认为问题不是“为什么我的序列弄乱了”-而是“为什么Django在插入行时为什么要尝试为id列提供一个值,而不是允许数据库在序列中插入下一个值”。

Django文档描述了用于确定在调用save()时应执行UPDATE还是INSERT的算法。

该算法涉及检查对象的“ id”字段是否已设置为某个值。 如果不是,则执行INSERT(可能未为“ id”字段指定值)。 如果已设置,则它首先尝试执行UPDATE; 如果这不会导致更新记录,则将执行INSERT(这一次可能会为“ id”字段指定一个值)。

正如Erwin的答案所指出的那样,您看到的错误消息表明它正在尝试在为'id'字段指定值时插入一行。

我注意到,似乎该算法在Django 1.6版中已更改。 以前,它先使用SELECT来查看是否存在记录,然后使用UPDATE或不插入INSERT。 如果自升级以来开始出现问题,则可能是原因。 文档说明:

在极少数情况下,即使数据库包含对象主键值的行,数据库也不会报告行已更新。 一个示例是PostgreSQL ON UPDATE触发器,该触发器返回NULL。 在这种情况下,可以通过将select_on_save选项设置为True来恢复到旧算法。

如果发生这种情况,那么它将解释您的症状:尝试更新数据库中的值时实际上会发生错误,而django会错误地认为该行不存在,然后尝试创建该行。

您可以通过将'select_on_save'设置为true来恢复到原来的行为来进行检查。

另一个可能的原因是,如果您的代码无意间将对象的'id'属性设置为某个值,然后调用save()。 根据该值是否已经存在于数据库中,这可能会导致各种问题。 特别是,这可能会导致创建一个行,该行的“ id”值位于与该列关联的序列的当前范围之前,因此以后您在尝试插入该行时会遇到错误。

另一个可能的原因可能是在先前从数据库加载的行上,对保存()使用了'force_insert'参数(因此,它实际上是您应该更新的现有行)。

问题的根源在这里(错误消息中的SQL命令):

INSERT INTO public.my_model(id, my_field, my_other_field)
VALUES ($1, $2, $3)
RETURNING id

由于你的id列似乎是一个serial类型, 不要手动插入值。 让默认值自动从序列中绘制。 应该:

INSERT INTO public.my_model(my_field, my_other_field)
VALUES ($1, $2)
RETURNING id;

这就是添加RETURNING id开头的全部要点:返回新生成的id 如果您自己输入值,则无需返回它。

固定

如果序列由于某种原因不同步,因为手动输入与nextval()的数字冲突,请运行一次此查询:

SELECT setval('my_model_id_seq', max(id)) FROM my_model;

这会将序列设置为当前最大值。 下一个呼叫是下一个号码,没有一次关闭错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM