La question est ancienne, mais nous avons reçu une nouvelle question d'un utilisateur désespéré sur dba.SE après avoir essayé d'appliquer ce qui est suggéré ici. Trouvez une réponse avec plus de détails et d'explications ici :
La réponse actuellement acceptée échouera dans la plupart des cas .
-
Typiquement, vous avez une
PRIMARY KEY
ouUNIQUE
contrainte sur unid
colonne, qui estNOT DEFERRABLE
par défaut. (OP mentionnereferences and constraints
.) Ces contraintes sont vérifiées après chaque ligne, vous obtenez donc très probablement une violation unique erreurs en essayant. Détails : -
En règle générale, on souhaite conserver l'ordre des lignes d'origine tout en comblant les lacunes. Mais l'ordre dans lequel les lignes sont mises à jour est arbitraire , conduisant à des nombres arbitraires. L'exemple démontré semble conserver la séquence d'origine car le stockage physique coïncide toujours avec l'ordre souhaité (lignes insérées dans l'ordre souhaité juste un instant plus tôt), ce qui n'est presque jamais le cas dans les applications du monde réel et totalement peu fiable.
L'affaire est plus compliquée qu'il n'y paraît au premier abord. Un solution (entre autres) si vous pouvez vous permettre de supprimer temporairement la contrainte PK / UNIQUE (et les contraintes FK associées) :
BEGIN;
LOCK tbl;
-- remove all FK constraints to the column
ALTER TABLE tbl DROP CONSTRAINT tbl_pkey; -- remove PK
-- for the simple case without FK references - or see below:
UPDATE tbl t -- intermediate unique violations are ignored now
SET id = t1.new_id
FROM (SELECT id, row_number() OVER (ORDER BY id) AS new_id FROM tbl) t1
WHERE t.id = t1.id;
-- Update referencing value in FK columns at the same time (if any)
SELECT setval('tbl_id_seq', max(id)) FROM tbl; -- reset sequence
ALTER TABLE tbl ADD CONSTRAINT tbl_pkey PRIMARY KEY(id); -- add PK back
-- add all FK constraints to the column back
COMMIT;
C'est aussi beaucoup plus rapide pour les grandes tables, car vérifier les contraintes PK (et FK) pour chaque ligne coûte beaucoup plus cher que de supprimer les contraintes et de les rajouter.
S'il existe des colonnes FK dans d'autres tables faisant référence à tbl.id
, utilisez CTE de modification des données pour tous les mettre à jour.
Exemple pour un tableau fk_tbl
et une colonne FK fk_id
:
WITH u1 AS (
UPDATE tbl t
SET id = t1.new_id
FROM (SELECT id, row_number() OVER (ORDER BY id) AS new_id FROM tbl) t1
WHERE t.id = t1.id
RETURNING t.id, t1.new_id -- return old and new ID
)
UPDATE fk_tbl f
SET fk_id = u1.new_id -- set to new ID
FROM u1
WHERE f.fk_id = u1.id; -- match on old ID
Plus d'informations dans la réponse référencée sur dba.SE .