quarta-feira, janeiro 21, 2026

PostgreSQL e IDENTITY: por que a sequência “desalinha” e como corrigir



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






Busca do Google

Custom Search