web-dev-qa-db-fra.com

l'auto-incrémentation postgres n'est pas mise à jour dans les insertions d'ID explicites

J'ai le tableau suivant en postgres:

CREATE TABLE "test" (
    "id" serial NOT NULL PRIMARY KEY,
    "value" text
)

Je fais les insertions suivantes:

insert into test (id, value) values (1, 'alpha')
insert into test (id, value) values (2, 'beta')

insert into test (value) values ('gamma')

Dans les 2 premiers encarts, je mentionne explicitement l'identifiant. Cependant, le pointeur d'incrémentation automatique de la table n'est pas mis à jour dans ce cas. Par conséquent, dans la troisième insertion, j'obtiens l'erreur:

ERROR:  duplicate key value violates unique constraint "test_pkey"
DETAIL:  Key (id)=(1) already exists.

Je n'ai jamais rencontré ce problème dans Mysql dans les moteurs MyISAM et INNODB. Explicite ou non, mysql met toujours à jour le pointeur d'auto-incrémentation en fonction de l'id de ligne max.

Quelle est la solution à ce problème dans les postgres? J'en ai besoin parce que je veux un contrôle plus strict pour certains identifiants dans ma table.

PDATE: J'en ai besoin car pour certaines valeurs j'ai besoin d'avoir un identifiant fixe. Pour d'autres nouvelles entrées, cela ne me dérange pas d'en créer de nouvelles.

Je pense que cela peut être possible en incrémentant manuellement le pointeur nextval vers max(id) + 1 chaque fois que j'insère explicitement les identifiants. Mais je ne sais pas comment faire ça.

55
jerrymouse

C'est ainsi que cela est censé fonctionner - next_val('test_id_seq') n'est appelé que lorsque le système a besoin d'une valeur pour cette colonne et que vous n'en avez pas fourni. Si vous fournissez une valeur, aucun appel de ce type n'est effectué et, par conséquent, la séquence n'est pas "mise à jour".

Vous pouvez contourner cela en manuellement paramètre la valeur de la séquence après votre dernière insertion avec des valeurs explicitement fournies:

SELECT setval('test_id_seq', (SELECT MAX(id) from "test"));
83
Milen A. Radev

Dans la version récente de Django, cette rubrique est abordée dans la documentation:

Django utilise PostgreSQL type de données SERIAL pour stocker les clés primaires à incrémentation automatique. Une colonne SERIAL est remplie de valeurs d'un séquence qui garde la trace de la prochaine valeur disponible. L'attribution manuelle d'une valeur à un champ à incrémentation automatique ne met pas à jour la séquence du champ, ce qui pourrait ultérieurement provoquer un conflit.

Réf: https://docs.djangoproject.com/en/dev/ref/databases/#manually-specified-autoincrement-pk

Il existe également une commande de gestion manage.py sqlsequencereset app_label ... qui est capable de générer des instructions SQL pour réinitialiser les séquences pour le (s) nom (s) d'application donné (s)

Réf: https://docs.djangoproject.com/en/dev/ref/Django-admin/#Django-admin-sqlsequencereset

Par exemple, ces instructions SQL ont été générées par manage.py sqlsequencereset my_app_in_my_project:

BEGIN;
SELECT setval(pg_get_serial_sequence('"my_project_aaa"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "my_project_aaa";
SELECT setval(pg_get_serial_sequence('"my_project_bbb"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "my_project_bbb";
SELECT setval(pg_get_serial_sequence('"my_project_ccc"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "my_project_ccc";
COMMIT;
3
illagrenan