PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Compactar ou renumerar IDs para todas as tabelas e redefinir sequências para max(id)?


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 uma PRIMARY KEY ou UNIQUE restrição em um id coluna, que é NOT DEFERRABLE por padrão. (OP menciona references 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 .