"ERREUR :impossible de créer un index unique
DÉTAIL :la table contient des valeurs en double."
Cette erreur est renvoyée par Postgres lorsqu'il rencontre des lignes en double dans une table de clé primaire en faisant échouer l'une de ces commandes REINDEX ou CREATE UNIQUE INDEX.
Pourquoi existe-t-il des lignes en double dans un tableau ?
Pas sûr exactement 🙂 ni aucune explication prouvée…
Deux choses à mon avis.
Premièrement, il peut s'agir d'une création d'index retardée ou si vous avez des séquences partagées dans une base de données, le partage sur deux tables de clés primaires différentes peut être la cause lors de la restauration des données dans la table (pg_restore). Deuxièmement, si une énorme transaction a lieu sur cette table et que quelqu'un a brusquement arrêté l'instance au niveau du backend, ce qui peut également empêcher l'index (clé primaire) de pointer vers la bonne ligne.
Comment y remédier ?
Eh bien, comme pratique courante, lorsque nous rencontrons des lignes en double dans une table (quelle qu'en soit la raison), nous filtrons d'abord les lignes en double et les supprimons, puis en faisant REINDEX devrait résoudre le problème.
Requête pour trouver des lignes en double :
sélectionnez count(*),primary_column du groupe table_name par primary_column ayant count(*)> 1 ;
Même après la suppression des lignes en double, REINDEX ou CREATE UNIQUE INDEX échoue, cela signifie que votre index n'est pas nettoyé correctement. La requête ci-dessus peut ne pas donner une sortie orientée résultat à 100% comme vous l'attendez, car la requête va sélectionner l'index qui est déjà corrompu avec des lignes en double. Voir le plan explicatif ci-dessous.
postgres=# expliquer sélectionner count(*),id du groupe duplicate_test par id ayant count(*)> 1 ;
QUERY PLAN
------------- -------------------------------------------------- ----------------------------------------
GroupeAgrégat (coût=0,00. .5042.90 rows=99904 width=4)
Filtre :(count(*)> 1)
-> Index Scan using duplicate_test_pkey on duplicate_test (cost=0.00..3044.82 rows=99904 width=4)
(3 rangées)
Nous devons attraper le CTID des lignes en double de la table principale et les supprimer avec une instruction conditionnelle comme CTID + PRIMARY KEY VALUE.
J'ai joué un peu avec pg_catalogs pour contourner la table de clé primaire afin de reproduire le scénario avec une erreur similaire. (S'il vous plaît, ne le faites pas)
postgres=# create unique index idup on duplicate_test(id);
ERREUR :impossible de créer l'index unique "idup"
DÉTAIL :la clé (id)=(10) est dupliquée.
Définition et données de ma table :
postgres=# d duplicate_test
Table "public.duplicate_test"
Colonne | Taper | Modificateurs
--------+---------+-----------
id | entier | non null
nom | text |
Index :
"duplicate_test_pkey" PRIMARY KEY, btree (id)
postgres=# select * from duplicate_test;
id | nom
----+---------
10 | Raghav ---Dupliquer
20 | Jean H
30 | Michel
10 | Raghav ---Dupliquer
(4 lignes)Maintenant, réparons cela….
Étape 1. Créez une nouvelle table à partir de la table affectée en extrayant seulement deux valeurs de colonne CTID et PRIMARY KEY.
postgres=# CREATE TABLE dupfinder AS SELECT ctid AS tid, id FROM duplicate_test;
SELECT 4Étape 2. Maintenant, exécutons la requête de recherche de doublons avec CTID pour obtenir les doublons exacts.
postgres=# select * from dupfinder x where exists (select 1 from dupfinder y where x.id =y.id and x.tid !=y.tid);
tid | id
-------+----
(0,1) | 10
(0,5) | 10
(2 rangées)Étape 3. Sur le résultat ci-dessus, vous pouvez maintenant supprimer une ligne de la table principale (table affectée) avec CTID.
postgres=# supprimer de duplicate_test où ctid='(0,5)' et id=10 ;
SUPPRIMER 1Étape 4. Maintenant, votre REINDEX ou CREATE UNIQUE INDEX réussira.
postgres=# crée un index unique idup sur duplicate_test(id);
CREATE INDEX
postgres=# select * from duplicate_test;
id | nom
----+---------
10 | Raghav
20 | Jean H
30 | Micheal
(3 rangées)Étape 5. N'oubliez pas de faire une ANALYSE DE VIDE immédiate sur la table pour mettre à jour les catalogues du système ainsi que le mouvement CTID.
Merci de partager vos commentaires.