Pytanie jest stare, ale otrzymaliśmy nowe pytanie od zdesperowanego użytkownika dba.SE po próbie zastosowania sugestii tutaj. Znajdź odpowiedź z więcej szczegółów i wyjaśnień tam :
obecnie zaakceptowana odpowiedź zawiedzie w większości przypadków .
-
Zazwyczaj masz
PRIMARY KEY
lubUNIQUE
ograniczenie naid
kolumna, która jestNOT DEFERRABLE
domyślnie. (OP wspomina oreferences and constraints
.) Takie ograniczenia są sprawdzane po każdym wierszu, więc najprawdopodobniej otrzymasz unikalne naruszenie błędy próbujące. Szczegóły: -
Zazwyczaj chce się zachować pierwotną kolejność wierszy jednocześnie zamykając luki. Ale kolejność, w jakiej wiersze są aktualizowane, jest dowolna , co prowadzi do dowolnych liczb. Zademonstrowany przykład wydaje się zachowywać pierwotną kolejność, ponieważ fizyczne przechowywanie nadal pokrywa się z pożądaną kolejnością (wiersze zostały wstawione w żądanej kolejności chwilę wcześniej), co prawie nigdy nie ma miejsca w rzeczywistych zastosowaniach i jest całkowicie zawodne.
Sprawa jest bardziej skomplikowana, niż mogłoby się początkowo wydawać. Jeden rozwiązanie (między innymi), jeśli możesz sobie pozwolić na tymczasowe usunięcie ograniczenia PK / UNIQUE (i powiązanych ograniczeń 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 też dużo szybciej w przypadku dużych tabel, ponieważ sprawdzenie ograniczeń PK (i FK) dla każdego wiersza kosztuje znacznie więcej niż usunięcie ograniczeń i dodanie ich z powrotem.
Jeśli w innych tabelach istnieją kolumny FK odwołujące się do tbl.id
, użyj CTE modyfikacji danych aby zaktualizować je wszystkie.
Przykład tabeli fk_tbl
i kolumna 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
Więcej w odpowiedzi na dba.SE .