web-dev-qa-db-fra.com

Pourquoi Postgres génère-t-il une valeur PK déjà utilisée?

J'utilise Django, et de temps en temps j'obtiens cette erreur:

IntegrityError: la valeur de clé en double viole la contrainte unique "myapp_mymodel_pkey"
DÉTAIL: La clé (id) = (1) existe déjà.

Ma base de données Postgres a en fait un objet myapp_mymodel avec la clé primaire de 1.

Pourquoi Postgres tenterait-il d'utiliser à nouveau cette clé primaire? Ou, est-ce probablement mon application (ou l'ORM de Django) qui cause cela?

Ce problème s'est produit 3 fois de suite à l'instant. Ce que j'ai trouvé, c'est que quand cela se produit se produit, cela se produit une ou plusieurs fois de suite pour une table donnée, puis pas de nouveau. Cela semble se produire pour chaque table avant qu'elle ne s'arrête complètement pendant des jours, se produisant pendant au moins une minute par table lorsqu'elle se produit, et ne se produisant que par intermittence (pas toutes les tables tout de suite).

Le fait que cette erreur soit si intermittente (ne s'est produit qu'environ 3 fois en 2 semaines - aucune autre charge sur la base de données, juste moi testant mon application) est ce qui me rend si méfiant d'un problème de bas niveau.

27
orokusaki

PostgreSQL n'essaiera pas d'insérer des valeurs en double de lui-même, c'est vous (votre application, ORM inclus) qui le fera.

Cela peut être soit une séquence alimentant les valeurs de l'ensemble PK dans la mauvaise position et la table contenant déjà la valeur égale à sa nextval() - ou simplement que votre application fait la mauvaise chose. Le premier est facile à corriger:

SELECT setval('your_sequence_name', (SELECT max(id) FROM your_table));

Le second signifie le débogage.

Django (ou tout autre framework populaire) ne réinitialise pas les séquences par lui-même - sinon nous aurions des questions similaires tous les deux jours.

36
dezso

Vous tentez probablement d'insérer une ligne dans une table pour laquelle la valeur de séquence de colonne série n'est pas mise à jour.

Pensez à suivre la colonne de votre table qui est la clé primaire définie par Django ORM pour postgres

id serial NOT NULL

Dont la valeur par défaut est définie sur

nextval('table_name_id_seq'::regclass)

La séquence n'est évaluée que lorsque le champ id est défini comme vide. Mais c'est un problème s'il y a déjà des entrées dans la table.

La question est pourquoi ces entrées précédentes n'ont-elles pas déclenché la mise à jour de la séquence? En effet, la valeur id a été explicitement fournie pour toutes les entrées précédentes.

Dans mon cas, ces entrées initiales ont été chargées à partir des appareils via les migrations.

Ce problème peut également devenir délicat via des entrées personnalisées avec une valeur PK aléatoire.

Dites par exemple. Il y a 10 entrées dans votre tableau. Vous effectuez une entrée explicite avec PK = 15. Les quatre insertions suivantes via le code fonctionneraient parfaitement, mais la 5ème soulèverait une exception.

DETAIL: Key (id)=(15) already exists.
8
Abhishek

Je me suis retrouvé ici avec la même erreur, qui se produisait rarement et était difficile à suivre, car je ne la cherchais pas où je devais.

L'erreur était la répétition JS qui faisait le POST au serveur deux fois! Donc, parfois, il vaut la peine de jeter un œil non seulement sur votre Django (ou tout autre web framework) vues et formulaires mais aussi ce qui se passe très en façade.

4
andilabs

Ouais chose bizarre. Dans mon cas, quelque chose semble mal lors du chargement des données dans les migrations. J'ai ajouté une migration vide et écrit les lignes pour ajouter des données initiales, 6 enregistrements dans mon cas.

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)

Ensuite, dans le panneau d'administration, j'ai essayé d'ajouter un nouvel élément et j'ai obtenu:

Premier essai:

DETAIL:  Key (id)=(1) already exists.

Tentatives ultérieures:

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.

Et enfin 7e et les fois sont tous réussis

Je dis donc qu'il y a peut-être quelque chose en rapport avec bulk_create car j'ai chargé 6 éléments là-bas. Cela peut être quelque chose de similaire dans votre projet Django provoquant cela.

Django 1.9 PostgreSQL 9.3.14

1
Bartosz Dabrowski