O Gerador De CPF O Gerador De CPF

CPF no Banco de Dados: CHAR, VARCHAR ou BIGINT?

A escolha do tipo de coluna para CPF afeta armazenamento, indexação e integridade dos dados. As três opções mais comuns são CHAR(11), VARCHAR(11) e BIGINT, cada uma com trade-offs diferentes.

Comparação rápida

CritérioCHAR(11)VARCHAR(11)BIGINT
Armazenamento11 bytes fixos12 a 13 bytes (1 a 2 de overhead)8 bytes
Zeros à esquerdaPreservaPreservaPerde
IndexaçãoExcelente (tamanho fixo)BoaExcelente
Validação de formatoSim (CHECK)Sim (CHECK)Parcial
Legibilidade em queriesAltaAltaBaixa (precisa LPAD)

CHAR(11) (recomendado)

CREATE TABLE pessoas (
    id SERIAL PRIMARY KEY,
    cpf CHAR(11) NOT NULL,
    CONSTRAINT chk_cpf_formato CHECK (cpf ~ '^\d{11}$')
);

CREATE UNIQUE INDEX idx_cpf ON pessoas (cpf);

CHAR(11) armazena exatamente 11 bytes, sem overhead de comprimento variável. Como todo CPF tem exatamente 11 dígitos, o tamanho fixo é uma vantagem: o banco sabe a largura de cada valor antes de ler, o que beneficia scans sequenciais e índices B-tree.

O CHECK com regex ^\d{11}$ garante na camada do banco que apenas 11 dígitos numéricos sejam aceitos, uma segunda barreira além da validação na aplicação.

Zeros à esquerda: CPFs como 001.002.003-00 são armazenados como 00100200300, preservando os zeros corretamente.

VARCHAR(11) (alternativa aceitável)

CREATE TABLE pessoas (
    id SERIAL PRIMARY KEY,
    cpf VARCHAR(11) NOT NULL,
    CONSTRAINT chk_cpf_formato CHECK (cpf ~ '^\d{11}$')
);

A diferença prática para CHAR(11) é pequena. VARCHAR adiciona 1 a 2 bytes de overhead para armazenar o comprimento, totalizando 12 a 13 bytes por valor. Em tabelas com milhões de registros, isso soma poucos megabytes.

A principal desvantagem: VARCHAR permite valores de comprimento variável, o que pode esconder bugs na aplicação (ex: CPF com 10 dígitos passando despercebido). O CHECK mitiga isso, mas CHAR(11) comunica melhor a intenção.

BIGINT (não recomendado)

CREATE TABLE pessoas (
    id SERIAL PRIMARY KEY,
    cpf BIGINT NOT NULL
);

BIGINT usa 8 bytes (menos que CHAR/VARCHAR) e a comparação numérica é marginalmente mais rápida. Porém:

Problema crítico: zeros à esquerda. O CPF 001.002.003-00 armazenado como BIGINT vira 100200300, um número de 9 dígitos. Para exibir corretamente, toda query precisa de LPAD:

SELECT LPAD(cpf::TEXT, 11, '0') AS cpf_formatado FROM pessoas;

Isso é fácil de esquecer e gera bugs silenciosos: o CPF parece válido com 9 ou 10 dígitos, mas está incompleto. Todo lugar que lê o CPF (relatórios, exports, APIs) precisa lembrar de aplicar o padding.

Validação parcial: BIGINT aceita qualquer número de 0 a 9.223.372.036.854.775.807. Sem CHECK, permite valores absurdos que um CPF nunca teria.

Com ou sem máscara?

Armazene apenas os 11 dígitos, sem pontos e traço. Motivos:

  • Economia de espaço: 11 chars vs 14 (com pontuação)
  • Indexação menor: índice B-tree sobre 11 bytes vs 14
  • Busca simples: WHERE cpf = '52998224725' sem precisar normalizar
  • Formatação na exibição: a máscara XXX.XXX.XXX-XX é aplicada na camada de apresentação
-- Armazenar: apenas dígitos
INSERT INTO pessoas (cpf) VALUES ('52998224725');

-- Formatar na query quando necessário
SELECT
    SUBSTR(cpf, 1, 3) || '.' ||
    SUBSTR(cpf, 4, 3) || '.' ||
    SUBSTR(cpf, 7, 3) || '-' ||
    SUBSTR(cpf, 10, 2) AS cpf_formatado
FROM pessoas;

Performance de indexação

Para tabelas grandes (milhões de registros), a escolha do tipo afeta o tamanho do índice:

TipoTamanho por entrada no índiceÍndice para 10M registros
CHAR(11)~20 bytes (11 + overhead B-tree)~200 MB
VARCHAR(11)~21 bytes (11 + 1 len + overhead)~210 MB
BIGINT~16 bytes (8 + overhead)~160 MB

A diferença de ~40 MB para 10 milhões de registros é irrelevante na prática. Priorize a correção dos dados (CHAR preserva zeros) sobre a micro-otimização.

Migração de BIGINT para CHAR

Se o banco já usa BIGINT e você precisa migrar:

-- Adicionar nova coluna
ALTER TABLE pessoas ADD COLUMN cpf_char CHAR(11);

-- Converter com padding de zeros
UPDATE pessoas SET cpf_char = LPAD(cpf::TEXT, 11, '0');

-- Verificar
SELECT COUNT(*) FROM pessoas WHERE LENGTH(cpf_char) != 11;

-- Trocar
ALTER TABLE pessoas DROP COLUMN cpf;
ALTER TABLE pessoas RENAME COLUMN cpf_char TO cpf;
ALTER TABLE pessoas ALTER COLUMN cpf SET NOT NULL;
ALTER TABLE pessoas ADD CONSTRAINT chk_cpf_formato CHECK (cpf ~ '^\d{11}$');
CREATE UNIQUE INDEX idx_cpf ON pessoas (cpf);

Para gerar CPFs de teste e popular o banco em ambiente de desenvolvimento, use o gerador de CPF.

Veja também: CPF com zeros à esquerda e como armazenar CPF com segurança.