Во-первых, следует ожидать пробелов в последовательности. Спросите себя, действительно ли вам нужно их удалить. Ваша жизнь становится проще, если вы просто живете с этим. Чтобы получить числа без пробелов, (часто лучше) альтернатива состоит в использовании VIEW
с row_number()
. Пример в этом связанном ответе:
Вот несколько рецептов для устранения пробелов.
1. Новый, нетронутый стол
Предотвращает осложнения с уникальными нарушениями и вздутием стола и быстро . Только для простых случаев, когда вы не связаны ссылками FK, представлениями таблицы или другими зависимыми объектами или одновременным доступом. Сделайте это за одну транзакцию, чтобы избежать несчастных случаев:
BEGIN;
LOCK tbl;
CREATE TABLE tbl_new (LIKE tbl INCLUDING ALL);
INSERT INTO tbl_new -- no target list in this case
SELECT row_number() OVER (ORDER BY id), data -- all columns in default order
FROM tbl;
ALTER SEQUENCE tbl_id_seq OWNED BY tbl_new.id; -- make new table own sequence
DROP TABLE tbl;
ALTER TABLE tbl_new RENAME TO tbl;
SELECT setval('tbl_id_seq', max(id)) FROM tbl; -- reset sequence
COMMIT;
CREATE TABLE tbl_new (LIKE tbl INCLUDING ALL)
копирует структуру вкл. ограничения и значения по умолчанию из исходной таблицы. Затем присвойте новому столбцу таблицы последовательность:
И сбросьте его до нового максимума:
Это дает преимущество в том, что новая таблица не имеет раздувания и кластеризована id
.
2. UPDATE
на месте
Это производит много мертвых строк и требует (авто) VACUUM
позже.
Если serial
столбец также PRIMARY KEY
(как в вашем случае) или имеет UNIQUE
ограничение, вы должны избегать уникальных нарушений в процессе. (Более дешевое) значение по умолчанию для ограничений PK / UNIQUE должно быть NOT DEFERRABLE
, что вызывает проверку после каждой отдельной строки. Все детали по этому связанному вопросу на SO:
Вы можете определить свое ограничение как DEFERRABLE
(что делает его более дорогим).
Или вы можете удалить ограничение и добавить его обратно, когда закончите:
BEGIN;
LOCK tbl;
ALTER TABLE tbl DROP CONSTRAINT tbl_pkey; -- remove PK
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;
SELECT setval('tbl_id_seq', max(id)) FROM tbl; -- reset sequence
ALTER TABLE tbl ADD CONSTRAINT tbl_pkey PRIMARY KEY(id); -- add PK back
COMMIT;
Ни не возможнопока естьFOREIGN KEY
ограниченияссылающихся столбец (ы)потому что ( за документацию ):
Столбцы, на которые ссылаются, должны быть столбцами неотложного ограничения уникального или первичного ключа в таблице, на которую ссылаются.
Вам необходимо (заблокировать все задействованные таблицы и) удалить / воссоздать ограничения FK и обновить все значения FK вручную (см. Вариант 3. ). Или вы должны убрать значения с пути за секунду, UPDATE
чтобы избежать конфликтов. Например, если у вас нет отрицательных чисел:
BEGIN;
LOCK tbl;
UPDATE tbl SET id = id * -1; -- avoid conflicts
UPDATE tbl t
SET id = t1.new_id
FROM (SELECT id, row_number() OVER (ORDER BY id DESC) AS new_id FROM tbl) t1
WHERE t.id = t1.id;
SELECT setval('tbl_id_seq', max(id)) FROM tbl; -- reset sequence
COMMIT;
Недостатки как упомянуто выше.
3. Таблица Temp, TRUNCATE
,INSERT
Еще один вариант, если у вас много оперативной памяти. Это сочетает в себе некоторые преимущества первых двух способов. Почти так же быстро , как вариант 1. и вы получите чистый, новый стол без наворотов , но сохранить все ограничения и зависимости в месте , как в варианте 2.
Тем не менее , в документации:
TRUNCATE
нельзя использовать для таблицы, имеющей ссылки на внешние ключи
из других таблиц, если только все такие таблицы не усекаются в одной и той же команде. Проверка достоверности в таких случаях потребует сканирования таблицы, и весь смысл не в том, чтобы сделать это.
Жирный акцент мой.
Вы можете временно удалить ограничения FK и использовать CTE, модифицирующие данные, чтобы обновить все столбцы FK:
SET temp_buffers = 500MB; -- example value, see 1st link below
BEGIN;
CREATE TEMP TABLE tbl_tmp AS
SELECT row_number() OVER (ORDER BY id) AS new_id, *
FROM tbl
ORDER BY id; -- order here to use index (if one exists)
-- drop FK constraints in other tables referencing this one
-- which takes out an exclusive lock on those tables
TRUNCATE tbl;
INSERT INTO tbl
SELECT new_id, data -- list all columns in order
FROM tbl_tmp; -- rely on established order in tbl_tmp
-- ORDER BY id; -- only to be absolutely sure (not necessary)
-- example for table "fk_tbl" with FK column "fk_id"
UPDATE fk_tbl f
SET fk_id = t.new_id -- set to new ID
FROM tbl_tmp t
WHERE f.fk_id = t.id; -- match on old ID
-- add FK constraints in other tables back
COMMIT;
Связанные, с более подробной информацией:
FOREIGN KEYS
настроены на,CASCADE
не могли бы вы просто перебрать старые первичные ключи и обновить их значения на месте (от старого значения до нового)? По сути, это вариант 3 безTRUNCATE tbl
заменыINSERT
с использованиемUPDATE
и без необходимости обновления внешних ключей вручную.