sql >> Databáze >  >> RDS >> PostgreSQL

Zkomprimovat nebo přečíslovat ID pro všechny tabulky a resetovat sekvence na max(id)?

Otázka je stará, ale dostali jsme novou otázku od zoufalého uživatele na dba.SE poté, co jsme se pokusili použít to, co je zde navrženo. Najděte odpověď s dalšími podrobnostmi a vysvětlením :

Aktuálně přijímaná odpověď ve většině případů selže .

  • Obvykle máte PRIMARY KEY nebo UNIQUE omezení na id sloupec, který je NOT DEFERRABLE ve výchozím stavu. (OP zmiňuje references and constraints .) Taková omezení jsou kontrolována po každém řádku, takže s největší pravděpodobností dojde k jedinečnému porušení chyby při pokusu. Podrobnosti:

  • Obvykle je potřeba zachovat původní pořadí řádků při uzavírání mezer. Pořadí, ve kterém jsou řádky aktualizovány, je však libovolné , což vede k libovolným číslům. Zdá se, že demonstrovaný příklad zachovává původní sekvenci, protože fyzické úložiště se stále shoduje s požadovaným pořadím (řádky byly vloženy v požadovaném pořadí jen o chvíli dříve), což v aplikacích v reálném světě téměř nikdy není a je zcela nespolehlivé.

Věc je složitější, než by se na první pohled mohlo zdát. Jedna řešení (mimo jiné), pokud si můžete dovolit dočasně odstranit omezení PK / UNIQUE (a související omezení 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;

To je také hodně rychlejší pro velké tabulky, protože kontrola omezení PK (a FK) pro každý řádek stojí mnohem víc než odstranění omezení a jejich přidání zpět.

Pokud jsou v jiných tabulkách sloupce FK odkazující na tbl.id , použijte CTE upravující data aktualizovat všechny.

Příklad tabulky fk_tbl a sloupec 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

Více v referované odpovědi na dba.SE .



  1. Vztah SQL one-to-many - Jak VYBRAT řádky v závislosti na více vlastnostech k mnoha?

  2. InsertAllOnSubmit vloží pouze první datový záznam

  3. Jak získat záznamy za posledních 30 dní

  4. Group_concat ekvivalent funkce MySQL v Oracle