PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Compatta o rinumera gli ID per tutte le tabelle e reimposta le sequenze su max(id)?

La domanda è vecchia, ma abbiamo ricevuto una nuova domanda da un utente disperato su dba.SE dopo aver provato ad applicare quanto suggerito qui. Trova una risposta con maggiori dettagli e spiegazioni laggiù :

La risposta attualmente accettata fallirà nella maggior parte dei casi .

  • In genere, hai una PRIMARY KEY o UNIQUE vincolo su un id colonna, che è NOT DEFERRABLE per impostazione predefinita. (OP menziona references and constraints .) Tali vincoli vengono controllati dopo ogni riga, quindi è molto probabile che si verifichi una violazione univoca errori nel tentativo. Dettagli:

  • In genere, si desidera mantenere l'ordine delle righe originale mentre colmi le lacune. Ma l'ordine di aggiornamento delle righe è arbitrario , portando a numeri arbitrari. L'esempio dimostrato sembra mantenere la sequenza originale perché l'archiviazione fisica coincide ancora con l'ordine desiderato (righe inserite nell'ordine desiderato solo un momento prima), cosa che non accade quasi mai nelle applicazioni del mondo reale e completamente inaffidabile.

La questione è più complicata di quanto possa sembrare a prima vista. Uno soluzione (tra le altre) se puoi permetterti di rimuovere temporaneamente il vincolo PK / UNIQUE (e i relativi vincoli FK):

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;

Anche questo è molto più veloce per le tabelle di grandi dimensioni, perché controllare i vincoli PK (e FK) per ogni riga costa molto di più che rimuovere i vincoli e aggiungerli nuovamente.

Se sono presenti colonne FK in altre tabelle che fanno riferimento a tbl.id , usa CTE di modifica dei dati per aggiornarli tutti.

Esempio per una tabella fk_tbl e una colonna 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

Maggiori informazioni nella risposta referenziata su dba.SE .