Se você usa PostgreSQL, provavelmente já viu (ou vai ver) um erro como:
ERROR: duplicate key value violates unique constraint
DETAIL: Key (id)=(2) already exists.
Esse erro costuma aparecer quando uma coluna IDENTITY (ou SERIAL) está tentando gerar um ID que já existe na tabela. Na prática, isso significa que a sequência interna do PostgreSQL ficou desalinhada com os dados.
Neste artigo você vai entender:
- o que é a sequência interna (sequence) do IDENTITY
- por que ela pode “perder o valor correto”
- como diagnosticar o problema
- como ajustar a sequência com segurança
1) O que é IDENTITY no PostgreSQL?
Uma coluna definida como GENERATED ... AS IDENTITY gera valores automaticamente usando uma sequence (sequência) por trás.
Exemplo:
CREATE TABLE exemplo (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
nome text NOT NULL
);
O PostgreSQL cria uma sequence interna (com um nome próprio) e passa a usar algo como:
nextval('nome_da_sequence')
para gerar IDs automaticamente.
2) Por que a sequência pode “desalinhar”?
O ponto mais importante é este:
A sequence é independente da tabela. Ela não “olha” os dados para decidir o próximo número.
Ou seja: a tabela pode ter ID 1000, mas se a sequence ainda estiver em 10, o próximo insert automático pode tentar gerar 11, 12... e em algum momento pode gerar um valor que já existe, causando erro.
Motivos comuns (na prática)
- Importação/carga de dados inserindo IDs manualmente (ex.: INSERT com
id=1000) - Restore de backup onde os dados voltam, mas a sequence não é ajustada
- Scripts de migração ou manutenção que inserem IDs explicitamente
- TRUNCATE/DELETE (a tabela muda, mas a sequence mantém o valor antigo)
- Testes/seed com dados fixos e IDs definidos “na mão”
3) Como diagnosticar se a sequence está errada
O diagnóstico clássico compara:
- maior ID da tabela vs
- valor atual da sequence
3.1 Descobrir o nome da sequence associada ao IDENTITY
SELECT pg_get_serial_sequence('schema.tabela', 'coluna_id');
Exemplo:
SELECT pg_get_serial_sequence('public.exemplo', 'id');
O retorno será o nome da sequence (por exemplo public.exemplo_id_seq).
3.2 Ver o maior ID existente na tabela
SELECT MAX(id) FROM public.exemplo;
3.3 Ver o valor atual da sequence
SELECT last_value FROM public.exemplo_id_seq;
Se last_value for menor que o MAX(id), você tem um desalinhamento.
4) Como ajustar a sequência do IDENTITY (forma segura)
A correção consiste em configurar a sequence para continuar a partir do maior ID existente.
4.1 Ajuste automático usando o máximo da tabela
SELECT setval(
pg_get_serial_sequence('schema.tabela', 'coluna_id'),
(SELECT COALESCE(MAX(coluna_id), 1) FROM schema.tabela)
);
Exemplo completo:
SELECT setval(
pg_get_serial_sequence('public.exemplo', 'id'),
(SELECT COALESCE(MAX(id), 1) FROM public.exemplo)
);
Após isso, o próximo INSERT automático usará MAX(id) + 1.
5) Caso especial: tabela vazia
Se a tabela estiver vazia e você quiser reiniciar a contagem, use o terceiro parâmetro do setval:
SELECT setval(
pg_get_serial_sequence('public.exemplo', 'id'),
1,
false
);
O false indica que o próximo valor gerado será exatamente 1.
6) Por que “buracos” em IDs são normais
Um detalhe que muita gente estranha:
Sequences não fazem rollback. Elas avançam mesmo se a transação falhar.
Exemplo:
BEGIN;
INSERT INTO public.exemplo (nome) VALUES ('Teste');
ROLLBACK;
Mesmo com rollback, a sequence pode ter incrementado. Isso gera “buracos” nos IDs, e isso é esperado. IDs não devem ser usados como números contábeis ou sequenciais perfeitos.
7) Boas práticas para evitar o problema
- Evite inserir IDs manualmente. Se precisar, ajuste a sequence depois.
- Após restore/importações, sempre valide
MAX(id)vs sequence. - Crie um checklist pós-deploy para checar sequences.
- Em migrações, prefira inserir sem ID e deixar o banco gerar.
Conclusão
Quando o PostgreSQL “tenta repetir um ID”, quase sempre o problema é a sequence interna do IDENTITY que ficou atrás do maior valor real da tabela.
A correção é simples e segura usando setval alinhado ao MAX(id). Com isso, você evita erros de chave duplicada e mantém o banco saudável mesmo após importações ou restores.
Se você quiser, eu posso publicar uma versão complementar mostrando:
- como resetar sequences em lote para um schema inteiro
- como fazer isso no contexto do Django (sqlsequencereset)
- como automatizar validação pós-deploy

Nenhum comentário:
Postar um comentário