A pergunta é antiga, mas recebemos uma nova pergunta de um usuário desesperado no dba.SE depois de tentar aplicar o que é sugerido aqui. Encontre uma resposta com mais detalhes e explicações lá :
A resposta atualmente aceita falhará na maioria dos casos .
-
Normalmente, você tem umaPRIMARY KEYouUNIQUErestrição em umidcoluna, que éNOT DEFERRABLEpor padrão. (OP mencionareferences and constraints.) Essas restrições são verificadas após cada linha, então você provavelmente terá violação única erros tentando. Detalhes:
-
Normalmente, deseja-se manter a ordem das linhas original enquanto fecha as lacunas. Mas a ordem em que as linhas são atualizadas é arbitrária , levando a números arbitrários. O exemplo demonstrado parece manter a sequência original porque o armazenamento físico ainda coincide com a ordem desejada (linhas inseridas na ordem desejada apenas um momento antes), o que quase nunca é o caso em aplicativos do mundo real e completamente não confiáveis.
O assunto é mais complicado do que pode parecer à primeira vista. Um solução (entre outras) se você puder remover a restrição PK / UNIQUE (e as restrições FK relacionadas) temporariamente:
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;
Isso também é muito mais rápido para tabelas grandes, porque verificar a(s) restrição(ões) PK (e FK) para cada linha custa muito mais do que remover a(s) restrição(ões) e adicioná-la(s) de volta.
Se houver colunas FK em outras tabelas referenciando
tbl.id , use CTEs de modificação de dados para atualizar todos eles. Exemplo para uma tabela
fk_tbl e uma coluna 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
Mais na resposta referenciada em dba.SE .