Въпросът е стар, но получихме нов въпрос от отчаян потребител на dba.SE, след като се опита да приложи това, което е предложено тук. Намерете отговор с повече подробности и обяснение там :
текущо приетият отговор ще се провали в повечето случаи .
-
Обикновено имате
PRIMARY KEY
илиUNIQUE
ограничение наid
колона, която еNOT DEFERRABLE
по подразбиране. (OP споменаваreferences and constraints
.) Такива ограничения се проверяват след всеки ред, така че най-вероятно ще получите уникално нарушение грешки при опит. Подробности: -
Обикновено човек иска да запази оригиналния ред на редовете при затваряне на пропуски. Но редът, в който се актуализират редовете, е произволен , което води до произволни числа. Демонстрираният пример изглежда запазва оригиналната последователност, тъй като физическото съхранение все още съвпада с желания ред (вмъкнати редове в желания ред само миг по-рано), което почти никога не е случаят в приложенията от реалния свят и е напълно ненадеждно.
Въпросът е по-сложен, отколкото може да изглежда на пръв поглед. Едно решение (наред с други), ако можете да си позволите временно да премахнете ограничението PK / UNIQUE (и свързаните ограничения 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;
Това също е много по-бързо за големи таблици, тъй като проверката на PK (и FK) ограничение(я) за всеки ред струва много повече от премахването на ограничението(ята) и добавянето му(тях) обратно.
Ако има FK колони в други таблици, препращащи към tbl.id
, използвайте CTE, модифициращи данни за да актуализирате всички тях.
Пример за таблица fk_tbl
и 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
Повече в референтния отговор на dba.SE .