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 KEY
ouUNIQUE
restrição em umid
coluna, que éNOT DEFERRABLE
por 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 .