Correction de la structure de la table pour éviter «Erreur: la valeur de la clé en double viole la contrainte unique»

15

J'ai une table qui est créée de cette façon:

--
-- Table: #__content
--
CREATE TABLE "jos_content" (
  "id" serial NOT NULL,
  "asset_id" bigint DEFAULT 0 NOT NULL,
   ...
  "xreference" varchar(50) DEFAULT '' NOT NULL,
  PRIMARY KEY ("id")
);

Plus tard, certaines lignes sont insérées en spécifiant l'id:

INSERT INTO "jos_content" VALUES (1,36,'About',...)

À un moment plus tard certains enregistrements sont insérés sans id et ils échouent avec l'erreur: Error: duplicate key value violates unique constraint.

Apparemment, l'id a été défini comme une séquence:

entrez la description de l'image ici

Chaque insertion ayant échoué augmente le pointeur dans la séquence jusqu'à ce qu'il augmente jusqu'à une valeur qui n'existe plus et que les requêtes réussissent.

SELECT nextval('jos_content_id_seq'::regclass)

Quel est le problème avec la définition de la table? Quelle est la manière intelligente de résoudre ce problème?

Valentin Despa
la source
Dans PostgreSQL, vous n'avez pas besoin de citer les noms de colonne et de table s'ils sont tous en minuscules.
Rodrigo

Réponses:

19

Rien ne va mal avec la définition de votre table.
(Sauf chapeau j'utiliser jos_content_idou quelque chose au lieu du nom de la colonne non-descriptive id.
Et je ne serais probablement utiliser au textlieu devarchar(50) .

Votre INSERTdéclaration est le problème.

Avec votre idcolonne définie comme serial, vous ne devez pas insérer de valeurs manuelles pour id. Ceux-ci peuvent entrer en collision avec la valeur suivante de la séquence associée.

Fournissez une liste explicite des colonnes cibles (ce qui est presque toujours une bonne idée pour les INSERTinstructions persistantes ) et omettez complètement les colonnes série .

INSERT INTO jos_content(asset_id, some_column, ...)
VALUES (36,'About',...);

Si vous avez immédiatement besoin de la ou des valeurs des colonnes générées automatiquement, utilisez la RETURNINGclause :

INSERT ...
RETURNING id;  -- possibly more

Plus de détails dans cette réponse connexe sur SO:

Si vous avez des entrées manuelles dans les serialcolonnes qui pourraient entrer en conflit plus tard, définissez votre séquence au maximum actuel idpour corriger cela une fois :

SELECT setval('jos_content_id_seq', max(id))
FROM   jos_content;

jos_content_id_seqest le nom par défaut d'une séquence appartenant à jos_content.id, que vous avez déjà trouvé dans la colonne par défaut. Semble être xhzt8_content_id_seqdans votre cas;


Mise à jour: Un problème similaire est apparu sur SO et j'ai trouvé une nouvelle solution:

Erwin Brandstetter
la source
Le texte n'est-il pas plus lent que varchar (50)?
Rodrigo
2
@Rodrigo: Pas à Postgres. Il y a un lien ci-dessus pour plus d'explications: dba.stackexchange.com/a/21496/3684 . Ou ici. dba.stackexchange.com/a/89433/3684
Erwin Brandstetter
Le dernier test ici < depesz.com/2010/03/02/charx-vs-varcharx-vs-varchar-vs-text > m'a convaincu que varchar (n) est plus rapide pour la plupart des champs où une restriction de taille est pratique (personnes noms, e-mails, adresses, noms des espèces, etc.). Le texte est plus rapide (ou le même) si vous ne vérifiez pas la longueur, semble-t-il.
Rodrigo