PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

IDs für alle Tabellen komprimieren oder neu nummerieren und Sequenzen auf max(id) zurücksetzen?

Die Frage ist alt, aber wir haben eine neue Frage von einem verzweifelten Benutzer auf dba.SE erhalten, nachdem wir versucht haben, das anzuwenden, was hier vorgeschlagen wird. Finden Sie eine Antwort mit weiteren Details und Erläuterungen dort :

Die aktuell akzeptierte Antwort wird in den meisten Fällen fehlschlagen .

  • Normalerweise haben Sie einen PRIMARY KEY oder UNIQUE Beschränkung auf eine id Spalte, die NOT DEFERRABLE ist standardmäßig. (OP erwähnt references and constraints .) Solche Beschränkungen werden nach jeder Zeile überprüft, sodass Sie höchstwahrscheinlich eindeutige Verletzung erhalten Fehler versuchen. Einzelheiten:

  • Normalerweise möchte man die ursprüngliche Reihenfolge beibehalten beim Schließen von Lücken. Aber die Reihenfolge, in der Zeilen aktualisiert werden, ist willkürlich , was zu willkürlichen Zahlen führt. Das gezeigte Beispiel scheint die ursprüngliche Reihenfolge beizubehalten, da die physische Speicherung immer noch mit der gewünschten Reihenfolge übereinstimmt (eingefügte Zeilen in der gewünschten Reihenfolge nur einen Moment früher), was in realen Anwendungen fast nie der Fall ist und völlig unzuverlässig ist.

Die Sache ist komplizierter, als es zunächst scheinen mag. Eine Lösung (unter anderem), wenn Sie es sich leisten können, die PK / UNIQUE-Einschränkung (und die damit verbundenen FK-Einschränkungen) vorübergehend zu entfernen:

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;

Das ist auch viel schneller für große Tabellen, da das Prüfen von PK (und FK)-Einschränkungen für jede Zeile viel mehr kostet, als die Einschränkung(en) zu entfernen und sie wieder hinzuzufügen.

Wenn es FK-Spalten in anderen Tabellen gibt, die auf tbl.id verweisen , verwenden Sie datenmodifizierende CTEs um sie alle zu aktualisieren.

Beispiel für eine Tabelle fk_tbl und eine FK-Spalte 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

Mehr in der referenzierten Antwort auf dba.SE .