De vraag is oud, maar we kregen een nieuwe vraag van een wanhopige gebruiker op dba.SE nadat we hadden geprobeerd toe te passen wat hier wordt voorgesteld. Vind een antwoord met meer details en uitleg daar :
Het momenteel geaccepteerde antwoord zal in de meeste gevallen mislukken .
-
Meestal heb je een
PRIMARY KEY
ofUNIQUE
beperking op eenid
kolom, dieNOT DEFERRABLE
. is standaard. (OP vermeldtreferences and constraints
.) Dergelijke beperkingen worden na elke rij gecontroleerd, dus u krijgt hoogstwaarschijnlijk een unieke overtreding fouten proberen. Details: -
Meestal wil men de originele volgorde van rijen . behouden terwijl gaten worden gedicht. Maar de volgorde waarin rijen worden bijgewerkt is willekeurig , wat leidt tot willekeurige getallen. Het gedemonstreerde voorbeeld lijkt de oorspronkelijke volgorde te behouden omdat fysieke opslag nog steeds samenvalt met de gewenste volgorde (rijen in de gewenste volgorde een moment eerder ingevoegd), wat bijna nooit het geval is in toepassingen in de echte wereld en volledig onbetrouwbaar.
De zaak is ingewikkelder dan het op het eerste gezicht lijkt. Eén oplossing (onder andere) als u het zich kunt veroorloven om de PK / UNIQUE-beperking (en gerelateerde FK-beperkingen) tijdelijk te verwijderen:
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;
Dit is ook veel sneller voor grote tabellen, omdat het controleren van PK (en FK)-beperkingen voor elke rij veel meer kost dan het verwijderen van de beperking(en) en het opnieuw toevoegen ervan.
Als er FK-kolommen in andere tabellen zijn die verwijzen naar tbl.id
, gebruik gegevensmodificerende CTE's om ze allemaal bij te werken.
Voorbeeld voor een tabel fk_tbl
en een FK-kolom 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
Meer in het verwezen antwoord op dba.SE .