PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Compacter ou renuméroter les identifiants de toutes les tables et réinitialiser les séquences à max(id) ?

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 ou UNIQUE contrainte sur un id colonne, qui est NOT DEFERRABLE par défaut. (OP mentionne references 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 .