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
neboUNIQUE
omezení naid
sloupec, který jeNOT DEFERRABLE
ve výchozím stavu. (OP zmiňujereferences 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 .