Quando você projeta uma tabela no banco de dados, como decide qual tipo numérico atribuir a cada coluna?
“Só usar INT pra inteiros.” “Melhor prevenir do que remediar — coloca BIGINT.” “Tem casa decimal, então FLOAT.” Se essas frases soam familiares, você não está sozinho. Mas esse tipo de decisão no automático pode causar problemas de desempenho, desperdício de armazenamento e até bugs críticos em cálculos financeiros lá na frente.
Pense nisso: trocar INT por BIGINT em uma única coluna de uma tabela com 100 milhões de linhas acrescenta aproximadamente 400 MB de armazenamento. Inclua dois índices e a diferença ultrapassa 1 GB. No outro extremo, usar INT em uma tabela de logs de alto volume que cresce milhões de linhas por dia pode estourar o limite em poucos anos, derrubando toda a aplicação.
E tem o FLOAT. A maioria dos devs sabe que 0.1 + 0.2 resulta em 0.30000000000000004 em vez de 0.3. Mesmo assim, colunas FLOAT continuam aparecendo em tabelas de pagamento em produção, acumulando erros de arredondamento silenciosamente até que os relatórios mensais de receita parem de bater.
Este artigo é um guia completo dos quatro tipos numéricos SQL mais importantes — INT, BIGINT, DECIMAL e FLOAT — cobrindo suas diferenças, trade-offs e regras práticas de decisão que você pode aplicar imediatamente.
Design de banco de dados é a base de qualquer sistema de software. Para fundamentos de design orientado a objetos, veja o Guia de Princípios SOLID. Para ajuda na escolha de uma linguagem de programação, confira o Guia Comparativo de Linguagens de Programação.
Tipos Numéricos SQL em Resumo
Comece pelo panorama geral. A tabela abaixo resume os principais tipos numéricos. Cada seção seguinte aprofunda os detalhes.
| Tipo | Tamanho | Faixa (aprox.) | Precisão | Uso Típico |
|---|---|---|---|---|
| TINYINT | 1 byte | 0 – 255 / -128 – 127 | Exata | Flags, códigos de status |
| SMALLINT | 2 bytes | 0 – 65.535 | Exata | Contadores pequenos |
| INT | 4 bytes | ~2,1 bilhões / ~4,2 bilhões | Exata | IDs, quantidades, contadores |
| BIGINT | 8 bytes | ~9,2 quintilhões | Exata | IDs de log, PKs em larga escala |
| DECIMAL(M,D) | Variável | M dígitos (D casas decimais) | Exata | Dinheiro, alíquotas, taxas |
| FLOAT | 4 bytes | ±3,4 × 10³⁸ | Aproximada | Temperatura, dados de sensores |
| DOUBLE | 8 bytes | ±1,7 × 10³⁰⁸ | Aproximada | Coordenadas GPS, estatística |
Os quatro tipos mais importantes no dia a dia são INT, BIGINT, DECIMAL e FLOAT. Domine esses quatro e você resolverá a grande maioria dos designs de banco de dados sem problemas.
Tipos Inteiros (Família INT) — O Ponto de Partida Padrão
Inteiros são o tipo numérico mais rápido, mais eficiente em armazenamento e livre de erros no SQL. Eles vencem em velocidade de cálculo, eficiência de índices e consumo de disco. Se uma coluna não precisa de casas decimais, um tipo inteiro é sempre a escolha certa.
O MySQL oferece cinco tamanhos de inteiro:
| Tipo | Tamanho | Faixa SIGNED | Faixa UNSIGNED |
|---|---|---|---|
| TINYINT | 1 byte | -128 a 127 | 0 a 255 |
| SMALLINT | 2 bytes | -32.768 a 32.767 | 0 a 65.535 |
| MEDIUMINT | 3 bytes | -8.388.608 a 8.388.607 | 0 a 16.777.215 |
| INT | 4 bytes | -2.147.483.648 a 2.147.483.647 | 0 a 4.294.967.295 |
| BIGINT | 8 bytes | -9,2 quintilhões a 9,2 quintilhões | 0 a ~18,4 quintilhões |
A regra de ouro: se pode ser representado como inteiro, use um tipo inteiro. Por exemplo, se o seu sistema armazena preços em centavos inteiros (R$), preco_centavos INT funciona perfeitamente. INT suporta até cerca de 2,1 bilhões, o que significa valores de até R$ 21 milhões em centavos — mais que suficiente para a maioria dos produtos de e-commerce.
Casos de uso comuns de inteiros:
- IDs (chaves primárias): user_id, product_id, order_id
- Quantidades: estoque, quantidade_carrinho
- Contadores: login_count, view_count, retry_count
- Códigos de status: status_pedido (0 = pendente, 1 = pago, 2 = enviado …)
- Flags booleanas: is_active, is_deleted (TINYINT com 0/1)
Não há motivo para usar DECIMAL ou FLOAT em nenhum desses casos. Inteiros são a escolha mais rápida e segura.
INT vs. BIGINT — “Melhor Prevenir” É Realmente Seguro?
O dilema mais comum na escolha de tipos inteiros é INT versus BIGINT. Resposta curta: INT é suficiente para a grande maioria dos casos de uso. A resposta longa mostra por que colocar BIGINT em tudo é um antipadrão caro.
Primeiro, vamos ter noção de escala. INT UNSIGNED suporta até 4,2 bilhões. A população do Brasil é de aproximadamente 215 milhões — apenas 5% da capacidade do INT. Um serviço web com 1 milhão de usuários poderia armazenar 4.000 registros de log por usuário e ainda ficaria dentro da faixa do INT. Para IDs de usuários, produtos e pedidos, INT quase sempre é mais que suficiente.
Então quando o BIGINT se torna necessário?
- Logs de acesso: Um site com 100 milhões de page views por mês acumula 1,2 bilhão de linhas por ano. Em 3–4 anos, o teto do INT fica à vista
- Dados de sensores IoT: 10.000 dispositivos enviando dados a cada segundo geram cerca de 315 bilhões de linhas por ano — muito além da faixa do INT
- IDs distribuídos (Snowflake, etc.): Esses codificam timestamp, worker ID e número de sequência em um único valor que pode ser extremamente grande
- IDs de transação: Um sistema de pagamento processando 1 milhão de transações por dia chega a 3,65 bilhões em 10 anos — perigosamente perto do limite superior do INT
Agora vamos quantificar o custo de “só coloca BIGINT em tudo”:
| Cenário | INT (4 bytes) | BIGINT (8 bytes) | Diferença |
|---|---|---|---|
| 100M linhas × 1 coluna | 381 MB | 762 MB | +381 MB |
| 100M linhas × 1 col + 2 índices | 1,14 GB | 2,29 GB | +1,14 GB |
| Memória de JOIN (estimado) | Base | ~1,5–2× | Menor eficiência de cache |
Em uma tabela de 100 milhões de linhas, trocar uma única coluna mais dois índices de INT para BIGINT desperdiça mais de 1,1 GB. Multiplique isso por várias colunas e várias tabelas, e o total pode chegar a dezenas de gigabytes — tudo isso aumenta I/O de disco, reduz a eficiência do buffer pool e deixa as queries mais lentas.
Guia prático de decisão:
| Finalidade da Coluna | Tipo Recomendado | Justificativa |
|---|---|---|
| ID de Usuário | INT UNSIGNED | Pouquíssimos serviços ultrapassam 4,2 bilhões de usuários |
| ID de Produto | INT UNSIGNED | Mesma lógica |
| ID de Pedido | INT UNSIGNED ou BIGINT | Depende do volume e tempo de vida |
| ID de Log de Acesso | BIGINT | Bilhões de linhas esperadas por ano |
| Snowflake / UUID Numérico | BIGINT | Valores inerentemente grandes |
Não baseie sua escolha apenas na contagem atual de linhas. O que importa é a taxa de crescimento ao longo de toda a vida operacional. Estime a quantidade anual de inserts, projete 10 anos à frente e verifique se INT UNSIGNED (4,2 bilhões) vai aguentar. Se não vai, comece com BIGINT desde o primeiro dia.
UNSIGNED — O Dobro da Faixa sem Custo Extra
No MySQL e MariaDB, adicionar UNSIGNED a uma coluna inteira remove a faixa negativa e dobra a faixa positiva sem custo adicional de armazenamento. Um INT regular vai de aproximadamente -2,1 bilhões a +2,1 bilhões; INT UNSIGNED vai de 0 a 4,2 bilhões — os mesmos 4 bytes.
Colunas como IDs, quantidades e contadores nunca são negativos. Não há motivo para não usar UNSIGNED nelas.
CREATE TABLE users (n id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,n age TINYINT UNSIGNED, u002du002d 0u0026lt;=255 é suficienten login_count INT UNSIGNED DEFAULT 0,n point INT UNSIGNED DEFAULT 0n);
Subtração entre duas colunas UNSIGNED pode causar underflow. No MySQL, SELECT a - b onde a < b produz um valor enorme (ou um erro) porque o resultado “dá a volta”. Se subtração for possível, use CAST(a AS SIGNED) - CAST(b AS SIGNED) ou considere manter a coluna como SIGNED.
Observação: PostgreSQL não suporta UNSIGNED. A solução comum é usar uma constraint CHECK (CHECK (id >= 0)) para garantir valores não-negativos no nível da aplicação.
DECIMAL (NUMERIC) — A Única Escolha Correta para Dinheiro
DECIMAL armazena números como valores exatos em base 10. Diferente do FLOAT, ele não converte para binário internamente, então 0,1 é armazenado como exatamente 0,1. Para qualquer coluna onde até uma fração de centavo importa — preços, notas fiscais, impostos, saldos — DECIMAL é o único tipo aceitável.
Por que não FLOAT? Veja o problema em ação:
u002du002d O que acontece com FLOATnSELECT CAST(0.1 AS FLOAT) + CAST(0.2 AS FLOAT);nu002du002d Resultado: 0.30000001192092896 (não é 0.3)nnu002du002d DECIMAL acertanSELECT CAST(0.1 AS DECIMAL(10,2)) + CAST(0.2 AS DECIMAL(10,2));nu002du002d Resultado: 0.30 (exatamente 0.30)
Como esse erro minúsculo importa na prática? Imagine uma loja online vendendo um produto de R$ 59,90, processando 50.000 pedidos por mês:
- Com FLOAT: Cada linha pode carregar um erro tão pequeno quanto +0,000001, mas ao longo de 50.000 linhas e múltiplos cálculos de ICMS, as discrepâncias de arredondamento se acumulam. Multiplique por centenas de SKUs ao longo de um ano, e o livro-caixa desvia por reais — o suficiente para disparar uma investigação de auditoria
- Com DECIMAL: Zero desvio. Toda agregação bate no centavo, toda vez
“É só um milionésimo de real.” Verdade — mas em contabilidade, se os livros não fecham no centavo, alguém precisa explicar por quê. “Usamos o tipo de coluna errado” não é uma resposta que nenhum auditor vai aceitar.
DECIMAL é declarado como DECIMAL(M, D) onde M é o total de dígitos e D é o número de casas decimais:
u002du002d DECIMAL(10,2): 10 dígitos no total, 2 casas decimaisnu002du002d Valor máximo: 99.999.999,99nnCREATE TABLE pedidos (n id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,n subtotal DECIMAL(10,2) NOT NULL, u002du002d Valor antes dos impostosn aliquota DECIMAL(5,4) NOT NULL, u002du002d Ex.: 0.1800 (ICMS 18%)n imposto DECIMAL(10,2) NOT NULL, u002du002d Valor do imposton total DECIMAL(10,2) NOT NULL u002du002d Total geraln);
DECIMAL e NUMERIC são sinônimos no padrão SQL. MySQL, PostgreSQL e SQL Server tratam ambos de forma idêntica. Escolha um e mantenha a consistência em todo o seu código.
Dimensionando o DECIMAL — Por que DECIMAL(18,10) Quase Sempre É Exagero
Outro erro comum é especificar precisão excessiva: DECIMAL(18,10) ou até DECIMAL(30,15) “por precaução”. Isso desperdiça armazenamento e deixa agregações mais lentas.
O tamanho de armazenamento do DECIMAL é proporcional à quantidade de dígitos. No MySQL, cada 9 dígitos consomem 4 bytes, com bytes extras para os dígitos restantes:
| Tipo | Armazenamento (MySQL) | Exemplo de Uso |
|---|---|---|
| DECIMAL(5,2) | 3 bytes | Percentuais (até 99,99%) |
| DECIMAL(10,2) | 5 bytes | Preços (até R$ 99.999.999,99) |
| DECIMAL(12,4) | 6 bytes | Taxas de câmbio (ex.: 5,3456) |
| DECIMAL(18,10) | 9 bytes | Exagero para a maioria das aplicações |
A abordagem correta é raciocinar de trás para frente a partir do valor máximo:
- Preços de e-commerce: Se o preço mais alto é de alguns milhões de reais, DECIMAL(10,2) cobre até R$ 99.999.999,99
- Alíquotas de ICMS: No Brasil, alíquotas estaduais variam de 7% a 25%, podendo chegar a mais com ICMS-ST. DECIMAL(5,4) suporta até 99,9999%
- Percentuais de desconto: 0–100% cabe em DECIMAL(5,2)
- Taxas de câmbio: USD/BRL a 5,0845 — DECIMAL(12,4) fornece espaço de sobra
- Criptomoedas: A menor unidade do Ethereum (wei = 10⁻¹⁸ ETH) genuinamente requer DECIMAL(36,18) — um caso raro onde precisão extrema é justificada
Usar DECIMAL(18,10) para uma coluna de preço de varejo é como imprimir um bilhete pessoal em papel A0. Dimensione a precisão corretamente e você vai economizar armazenamento e acelerar queries.
FLOAT / DOUBLE — Velocidade ao Custo da Exatidão
FLOAT e DOUBLE são tipos de ponto flutuante que representam decimais usando o formato binário IEEE 754. Isso lhes dá uma grande vantagem e uma limitação inerente.
A vantagem é clara: apenas 4 bytes (FLOAT) ou 8 bytes (DOUBLE) podem representar uma faixa enorme de valores. Só o FLOAT cobre ±3,4 × 10³⁸, e o hardware de ponto flutuante da CPU faz a aritmética extremamente rápida.
A limitação é que eles armazenam aproximações, não valores exatos. O decimal 0,1 se torna o binário infinitamente repetitivo 0,000110011001100… que precisa ser arredondado para caber em um número finito de bits. Essa é a causa raiz do famoso problema “0,1 + 0,2 ≠ 0,3”.
FLOAT é a escolha certa quando pequenos erros de arredondamento não afetam o resultado:
- Leituras de temperatura: Um sensor industrial reporta 23,45 °C com precisão intrínseca de ±0,1 °C. Um erro de armazenamento de ±0,0001 °C é irrelevante
- Coordenadas GPS: Seis casas decimais em latitude/longitude correspondem a ~11 cm de precisão. DOUBLE preserva até 15 dígitos significativos — precisão submilimétrica que vai muito além de qualquer necessidade real
- Features de machine learning: Modelos de ML têm milhões a bilhões de parâmetros; arredondamento em nível micro em qualquer peso individual tem impacto desprezível na acurácia geral
- Simulações físicas: Dinâmica de fluidos e análise estrutural dependem da velocidade do FLOAT, com o erro controlado no nível do algoritmo
- Agregações estatísticas: Médias, desvios padrão e correlações são calculados a partir de dados que já contêm ruído estatístico
A escolha entre FLOAT e DOUBLE se resume a precisão e armazenamento:
| Tipo | Tamanho | Dígitos Significativos | Quando Escolher |
|---|---|---|---|
| FLOAT | 4 bytes | ~7 | Sensível a armazenamento, dados de sensores |
| DOUBLE | 8 bytes | ~15 | Alta precisão necessária: GPS, computação científica |
Armazenar coordenadas GPS como FLOAT dá apenas ~7 dígitos significativos — aproximadamente 11 m de precisão. Para uma aplicação de mapas, DOUBLE (~15 dígitos, sub-milimétrico) é a escolha óbvia. Por outro lado, armazenar leituras de sensores de temperatura como DOUBLE é inútil quando o próprio sensor tem precisão de apenas ±0,5 °C — FLOAT é mais que suficiente.
FLOAT vs. DECIMAL — Guia Rápido de Decisão
Na prática, você precisa tomar essa decisão rápido e com confiança. Aqui está uma tabela de referência:
| Caso de Uso | DECIMAL | FLOAT / DOUBLE |
|---|---|---|
| Preços, notas fiscais, faturamento | ✓ | ✗ (nunca) |
| Alíquotas de ICMS, descontos | ✓ | ✗ |
| Pontos de fidelidade / milhas | ✓ (se fracionário) | ✗ |
| Peso de estoque (kg) | ✓ | △ |
| Temperatura / umidade | △ | ✓ |
| Coordenadas GPS | △ | ✓ (DOUBLE) |
| Dados de sensores | △ | ✓ |
| Features de ML / scores | ✗ | ✓ |
| Valores estatísticos (média, etc.) | △ | ✓ |
A regra geral cabe em três linhas:
- Envolve dinheiro → DECIMAL
- Medição ou ciência → FLOAT / DOUBLE
- Não tem certeza → DECIMAL (erre pelo lado seguro)
Memorize essas três regras e dificilmente vai errar.
Cinco Erros Comuns de Design
Erros de tipo numérico costumam ser invisíveis durante o desenvolvimento e só aparecem em produção. Aqui estão os cinco mais frequentes.
Erro 1: Colocar BIGINT em Toda Coluna
A mentalidade de “maior é mais seguro” leva equipes a colocar BIGINT como padrão em toda coluna inteira. Como mostrado acima, isso pode desperdiçar mais de 1 GB a cada 100 milhões de linhas em uma única coluna com índices. Em 10 tabelas com 3 colunas BIGINT cada, são aproximadamente 12 GB de armazenamento desperdiçado — impactando diretamente custos de hospedagem em nuvem e eficiência do buffer pool.
Erro 2: Usar FLOAT para Dinheiro
Este é o erro mais perigoso da lista. Muitas vezes passa em todos os testes unitários porque os erros de arredondamento são invisíveis em pequena escala. O problema aparece em produção quando o volume de transações cresce: “A receita mensal não bate com os depósitos reais no banco.” A análise de causa raiz leva dias, e corrigir retroativamente dados financeiros armazenados em FLOAT é extremamente difícil.
Erro 3: Superdimensionar a Precisão do DECIMAL
Definir DECIMAL(30,15) “por precaução” desperdiça armazenamento e deixa queries de agregação mais lentas. Fora criptomoedas (onde 18 casas decimais são genuinamente necessárias), pouquíssimos domínios de negócio precisam de mais de 4 casas decimais.
Erro 4: Não Prever o Overflow do INT
Uma tabela pode começar com apenas algumas centenas de inserts por dia, mas o crescimento pode ser exponencial. INT SIGNED suporta até ~2,1 bilhões. Com AUTO_INCREMENT a 50.000 inserts/dia, isso dá 117 anos de folga — mas dumps de dados de teste, gaps de ID e crescimento inesperado podem consumir essa margem mais rápido do que o esperado. Monitore regularmente os valores máximos de AUTO_INCREMENT.
Erro 5: Tipos Diferentes em JOINs
Se pedidos.user_id é INT mas users.id é BIGINT, cada JOIN dispara uma conversão implícita de tipo. No MySQL, isso pode impedir o otimizador de usar índices, transformando uma query de milissegundos em um full table scan de vários segundos. Sempre garanta que colunas usadas em JOINs tenham exatamente o mesmo tipo.
Tipos Recomendados por Caso de Uso
Use esta tabela de referência rápida ao projetar novas tabelas:
| Finalidade da Coluna | Tipo Recomendado | Observações |
|---|---|---|
| ID de Usuário | INT UNSIGNED | 4,2 bilhões é mais que suficiente |
| ID de Produto | INT UNSIGNED | Mesma lógica |
| ID de Log / Evento | BIGINT UNSIGNED | Bilhões de linhas por ano |
| Snowflake ID | BIGINT | Valores inerentemente grandes |
| Preço de produto | DECIMAL(10,2) | Máximo R$ 99.999.999,99 |
| Alíquota de ICMS | DECIMAL(5,4) | Ex.: 0.1800 (18%) |
| Percentual de desconto | DECIMAL(5,2) | Ex.: 15,50% |
| Taxa de câmbio | DECIMAL(12,4) | Ex.: 5,0845 |
| Quantidade em estoque (inteiro) | INT UNSIGNED | Sem casas decimais |
| Peso (kg) | DECIMAL(8,3) | Ex.: 12345,678 |
| Temperatura | FLOAT | Precisão do sensor é suficiente |
| GPS lat / lng | DOUBLE | Alta precisão necessária |
| Feature de ML | FLOAT | Velocidade acima de precisão |
| Pontos (inteiro) | INT UNSIGNED | Sem frações |
| Pontos (fracionário) | DECIMAL(10,2) | Milhas aéreas, etc. |
| Posição em ranking | INT UNSIGNED | Rankings nunca são negativos |
| Flag booleana (0/1) | TINYINT UNSIGNED | BOOLEAN do MySQL por baixo dos panos |
| Código de status | TINYINT ou SMALLINT | Dimensione para a faixa de valores |
Compartilhar uma tabela como essa com a equipe elimina a maioria dos debates sobre escolha de tipo durante o code review.
CREATE TABLE — Exemplos do Mundo Real
Por fim, aqui estão três definições de tabela prontas para produção. Observe como cada coluna usa o menor tipo adequado.
Exemplo 1: Produtos de E-commerce
CREATE TABLE produtos (n id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,n nome VARCHAR(255) NOT NULL,n preco DECIMAL(10,2) NOT NULL DEFAULT 0.00, u002du002d Dinheiro = DECIMALn aliq_icms DECIMAL(5,4) NOT NULL DEFAULT 0.1800, u002du002d 18% = 0.1800n estoque INT UNSIGNED NOT NULL DEFAULT 0, u002du002d Quantidade inteiran peso_kg DECIMAL(8,3), u002du002d Peso para freten avaliacao FLOAT, u002du002d Nota média dos usuáriosn ativo TINYINT UNSIGNED NOT NULL DEFAULT 1, u002du002d Flag booleanan criado_em DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMPn) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Exemplo 2: Logs de Acesso
CREATE TABLE logs_acesso (n id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, u002du002d Alto volumen user_id INT UNSIGNED, u002du002d Mesmo tipo de users.idn status_code SMALLINT UNSIGNED NOT NULL, u002du002d HTTP 200, 404, 500...n resposta_ms INT UNSIGNED, u002du002d Tempo de resposta em msn criado_em DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,n INDEX idx_user (user_id),n INDEX idx_criado (criado_em)n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Exemplo 3: Leituras de Sensores IoT
CREATE TABLE leituras_sensores (n id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,n dispositivo_id INT UNSIGNED NOT NULL,n temperatura FLOAT, u002du002d Precisão do sensor é suficienten umidade FLOAT, u002du002d Idemn latitude DOUBLE, u002du002d GPS precisa de alta precisãon longitude DOUBLE, u002du002d Idemn bateria_pct TINYINT UNSIGNED, u002du002d Bateria 0-100%n registrado_em DATETIME(3) NOT NULL, u002du002d Precisão de milissegundosn INDEX idx_disp_tempo (dispositivo_id, registrado_em)n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
As três tabelas compartilham um traço comum: cada coluna é definida com o menor tipo que atende ao seu propósito. IDs escalam com INT ou BIGINT conforme a necessidade, dinheiro usa DECIMAL, medições usam FLOAT/DOUBLE e flags usam TINYINT. É assim que um schema bem projetado se parece.
Resumo — Escolha de Tipo É Engenharia de Performance
Escolher um tipo numérico SQL não é apenas uma decisão de formatação. Afeta eficiência de armazenamento, performance de índices, velocidade de queries, precisão dos dados e escalabilidade futura — tudo ao mesmo tempo.
O essencial em quatro linhas:
- Se cabe em inteiro, use um tipo inteiro (mais rápido, menor, zero erro)
- INT para a maioria das colunas, BIGINT para logs de alto volume (o armazenamento dobra)
- DECIMAL para dinheiro — sem exceções (erros de FLOAT são devastadores em finanças)
- FLOAT / DOUBLE para ciência e medições (velocidade e faixa onde aproximação é aceitável)
Acima de tudo, o princípio orientador é: escolha o menor tipo que atende aos seus requisitos. Tipos superdimensionados desperdiçam armazenamento, reduzem eficiência de cache e deixam queries mais lentas. Tipos subdimensionados arriscam overflow. Acertar significa estimar a natureza dos seus dados (inteiro vs. decimal), a faixa de valores, a tolerância a erros e a taxa de crescimento ao longo da vida útil do sistema.
Design de tipos numéricos é um trabalho sem glamour, mas acertar desde o primeiro dia te poupa de degradação de performance, inchaço de armazenamento e incidentes em produção lá na frente. Toda vez que você escrever um CREATE TABLE, pergunte: “Esse tipo é realmente o adequado?” Só esse hábito já vai elevar a qualidade do design do seu banco de dados.
FAQ
P. Qual deve ser meu primeiro raciocínio quando não sei qual tipo usar?
R. Comece perguntando: “Esse valor pode ser representado como inteiro?” Se sim, vá de tipo INT. Depois pergunte: “Envolve dinheiro?” Se sim, DECIMAL é a única resposta. Todo o resto com casas decimais — temperaturas, coordenadas, scores — aponta para FLOAT ou DOUBLE. Siga essa sequência e 95% das decisões são instantâneas.
P. Migrar de INT para BIGINT depois é muito trabalhoso?
R. O ALTER TABLE ... MODIFY COLUMN do MySQL pode alterar o tipo, mas em tabelas grandes trava a tabela por minutos ou até horas. Ferramentas como pt-online-schema-change ou gh-ost fazem a migração com downtime quase zero, mas ainda exigem planejamento cuidadoso. Acertar o tipo desde o início é sempre mais barato do que refatorar depois.
P. O PostgreSQL não suporta UNSIGNED — o que devo fazer?
R. Correto. A abordagem padrão no PostgreSQL é usar uma constraint CHECK (CHECK (id >= 0)) para garantir valores não-negativos. Como o INT do PostgreSQL suporta até ~2,1 bilhões, é suficiente para a maioria das cargas de trabalho. Se você genuinamente precisa da faixa de 4,2 bilhões, mude para BIGINT.
P. O erro de arredondamento do FLOAT é tão ruim assim na prática?
R. FLOAT (4 bytes) tem cerca de 7 dígitos significativos. Armazene 123456,789 em FLOAT e você pode receber de volta 123456,7890625. Para dados de sensores ou medições científicas — onde o próprio instrumento tem precisão limitada — isso é irrelevante. Para cálculos financeiros, significa notas fiscais com erro de centavos e totais mensais que não fecham. A regra é simples: nunca use FLOAT para dinheiro.
P. DECIMAL é realmente mais lento que INT? Quanto?
R. Em queries pesadas de agregação (SUM, AVG sobre milhões de linhas), DECIMAL pode ser 1,2–2× mais lento que INT. Para cargas OLTP típicas — SELECTs e INSERTs individuais — a diferença é desprezível. Algumas equipes armazenam preços como centavos inteiros (ex.: R$ 19,99 → 1999) para ganhar a velocidade do INT, mas isso quebra no momento em que você precisa de suporte a múltiplas moedas ou cálculos sub-centavo. Começar com DECIMAL é a aposta mais segura a longo prazo.

Leave a Reply