Spørgsmålet er gammelt, men vi fik et nyt spørgsmål fra en desperat bruger på dba.SE efter at have forsøgt at anvende det, der foreslås her. Find et svar med flere detaljer og forklaring derovre :
Det aktuelt accepterede svar vil mislykkes i de fleste tilfælde .
-
Typisk har du en
PRIMARY KEY
ellerUNIQUE
begrænsning på etid
kolonne, som erNOT DEFERRABLE
som standard. (OP nævnerreferences and constraints
.) Sådanne begrænsninger kontrolleres efter hver række, så du højst sandsynligt får unik overtrædelse fejl ved at prøve. Detaljer: -
Typisk ønsker man at beholde den oprindelige rækkefølge mens der lukkes huller. Men rækkefølgen, som rækker opdateres i, er vilkårlig , hvilket fører til vilkårlige tal. Det viste eksempel ser ud til at bibeholde den oprindelige rækkefølge, fordi fysisk lagring stadig falder sammen med den ønskede rækkefølge (indsatte rækker i ønsket rækkefølge blot et øjeblik tidligere), hvilket næsten aldrig er tilfældet i applikationer fra den virkelige verden og fuldstændig upålidelig.
Sagen er mere kompliceret, end den umiddelbart kan se ud. En løsning (blandt andre), hvis du har råd til midlertidigt at fjerne PK / UNIQUE-begrænsningen (og relaterede FK-begrænsninger):
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;
Dette er også meget hurtigere for store borde, fordi kontrol af PK (og FK) begrænsning(er) for hver række koster meget mere end at fjerne begrænsning(erne) og tilføje dem igen.
Hvis der er FK-kolonner i andre tabeller, der refererer til tbl.id
, brug datamodificerende CTE'er for at opdatere dem alle.
Eksempel på en tabel fk_tbl
og en FK-kolonne 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
Mere i referencede svar på dba.SE .