Por que normalizar: redundância controlada e significado preservado
Normalização é um conjunto de técnicas para organizar tabelas de forma que cada fato seja armazenado uma única vez (ou o mínimo possível), reduzindo redundância e evitando inconsistências. O objetivo não é “quebrar tabelas por quebrar”, e sim garantir que os dados representem corretamente o negócio ao longo do tempo, mesmo com inserções, atualizações e remoções.
Na prática, normalizar ajuda a evitar três tipos clássicos de problemas (anomalias):
- Anomalia de inserção: para cadastrar um fato, você é obrigado a cadastrar outro que ainda não existe.
- Anomalia de atualização: o mesmo fato aparece em vários lugares e pode ficar divergente.
- Anomalia de remoção: ao apagar um registro, você perde um fato que deveria permanecer.
Dependências funcionais (DF): a base aplicada da normalização
Uma dependência funcional descreve uma regra do tipo: “se eu souber X, eu determino Y”. Escrevemos como X -> Y. Isso significa que, dentro de uma tabela, para cada valor de X existe um único valor correspondente de Y.
Como identificar DF no dia a dia
- Pergunte: “Se eu tiver este identificador, consigo descobrir aquele atributo sem ambiguidade?”
- Se a resposta for “sim, sempre”, há uma DF.
- Se a resposta for “depende” (do tempo, do contexto, de outro atributo), não é DF direta; pode haver DF composta ou regra temporal.
Exemplo simples de DF
Suponha uma tabela de produtos:
Produto(id_produto, nome, categoria)Se id_produto identifica unicamente o produto, então:
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
id_produto -> nomeid_produto -> categoria
Agora, se você tiver uma tabela de itens de pedido:
ItemPedido(id_pedido, id_produto, qtd, preco_unitario)É comum que:
(id_pedido, id_produto) -> qtd(id_pedido, id_produto) -> preco_unitario
Ou seja, a combinação (id_pedido, id_produto) determina os atributos do item.
Primeira Forma Normal (1FN): valores atômicos e repetição controlada
A 1FN exige que cada coluna tenha valores atômicos (não “listas” dentro de uma célula) e que não existam grupos repetidos de colunas para o mesmo tipo de informação.
Sinais de violação de 1FN
- Colunas como
telefone1,telefone2,telefone3. - Campo com lista:
telefones = "(11)9999-0000; (11)9888-0000". - Campo com estrutura embutida:
endereco = "Rua X, 10 - Centro"quando o negócio precisa pesquisar/validar partes do endereço separadamente.
Exemplo (não normalizado) e correção para 1FN
Tabela problemática:
Cliente(id_cliente, nome, telefones)Exemplo de dado:
(1, 'Ana', '(11)9999-0000; (11)9888-0000')Problemas práticos:
- Difícil validar formato e unicidade de cada telefone.
- Difícil buscar clientes por um telefone específico.
- Atualizar um telefone exige manipular string.
Correção (1FN):
Cliente(id_cliente, nome) TelefoneCliente(id_cliente, telefone)Agora cada telefone é um registro, permitindo validação, índices e consultas simples.
Segunda Forma Normal (2FN): remover dependências parciais em chaves compostas
A 2FN se aplica quando a tabela tem chave primária composta (ou uma chave candidata composta). Ela exige que todo atributo não-chave dependa da chave inteira, e não apenas de uma parte.
Exemplo clássico: Item de Pedido com dados do Produto
Tabela problemática:
ItemPedido(id_pedido, id_produto, nome_produto, categoria, qtd, preco_unitario)Suponha que a chave do item seja (id_pedido, id_produto). Observe as dependências:
(id_pedido, id_produto) -> qtd(id_pedido, id_produto) -> preco_unitarioid_produto -> nome_produto(depende só de parte da chave)id_produto -> categoria(depende só de parte da chave)
Isso é dependência parcial: atributos do produto estão “pendurados” apenas em id_produto, não na chave inteira do item.
Anomalias geradas por violar 2FN
- Atualização: se o nome do produto mudar, você precisa atualizar em todos os itens de pedido onde ele aparece; risco de divergência.
- Inserção: para cadastrar um novo produto, você teria que criar um item de pedido (o que não faz sentido).
- Remoção: se você apagar o último item de pedido de um produto, pode perder a informação do produto.
Correção passo a passo para 2FN
Passo 1: identifique a chave composta e liste os atributos não-chave.
Passo 2: encontre atributos que dependem apenas de parte da chave.
Passo 3: mova esses atributos para uma tabela onde a parte determinante seja chave.
Modelo corrigido:
Produto(id_produto, nome_produto, categoria) ItemPedido(id_pedido, id_produto, qtd, preco_unitario)Agora:
- Dados do produto ficam em
Produto. - Dados do item (quantidade e preço do item naquele pedido) ficam em
ItemPedido.
Terceira Forma Normal (3FN): remover dependências transitivas
A 3FN exige que atributos não-chave não dependam de outros atributos não-chave. Em termos de DF: se chave -> A e A -> B, então B depende transitivamente da chave via A. Em 3FN, B não deve ficar na mesma tabela se sua “origem” é A.
Exemplo: Cliente com dados de Cidade/UF
Tabela problemática:
Cliente(id_cliente, nome, cep, cidade, uf)Dependências típicas:
id_cliente -> cepcep -> cidadecep -> uf
Logo, id_cliente -> cidade e id_cliente -> uf de forma transitiva (via cep).
Anomalias geradas por violar 3FN
- Atualização: se a cidade associada a um CEP for corrigida, você precisa atualizar todos os clientes com aquele CEP.
- Inserção: para cadastrar um CEP novo, você teria que cadastrar um cliente.
- Remoção: ao remover o último cliente de um CEP, você perde o mapeamento CEP->cidade/UF.
Correção passo a passo para 3FN
Passo 1: encontre atributos não-chave que determinam outros atributos não-chave.
Passo 2: extraia para uma tabela própria do determinante.
Modelo corrigido:
CEP(cep, cidade, uf) Cliente(id_cliente, nome, cep)Assim, cidade e uf passam a ser obtidos via relacionamento com CEP, e o mapeamento fica consistente.
Checklist prático: conduzindo uma tabela por 1FN, 2FN e 3FN
1) Verificação de 1FN
- Há colunas com listas, múltiplos valores ou estruturas concatenadas?
- Há colunas repetidas do mesmo tipo (
x1,x2,x3)? - Se sim, separar em linhas (tabela filha) ou em atributos atômicos conforme a necessidade de consulta/validação.
2) Verificação de 2FN
- A chave é composta?
- Algum atributo não-chave depende de apenas uma parte da chave?
- Se sim, mover o atributo para uma tabela onde essa parte seja chave.
3) Verificação de 3FN
- Algum atributo não-chave determina outro atributo não-chave?
- Existe uma DF do tipo
A -> Bonde A não é chave? - Se sim, criar tabela para A e referenciar a partir da tabela original.
Como reconhecer desnormalização indevida (e corrigir)
Sinais comuns no modelo físico
- Campos “espelho” repetidos em várias tabelas (ex.:
nome_clienteem Pedido, ItemPedido, Fatura). - Campos derivados persistidos sem regra (ex.:
total_pedidoarmazenado sem controle de consistência). - Dependências escondidas: um atributo que “parece” do registro, mas na verdade depende de outro (ex.:
ufdepende decep). - Atualizações frequentes e divergências: o mesmo dado aparece com grafias diferentes em registros distintos.
Correções típicas
- Substituir campos espelho por referência e obter o valor via consulta/junção.
- Se precisar manter um derivado (por performance), criar regra explícita de manutenção (ver seção de desnormalização consciente).
- Extrair tabelas de referência (ex.: CEP, Categoria, Status) quando houver DF clara.
Quando desnormalizar conscientemente (performance) e como mitigar riscos
Desnormalização é aceitável quando há uma necessidade objetiva, geralmente ligada a performance de leitura, redução de junções em consultas críticas, ou relatórios com alto volume. A decisão deve ser consciente: você troca parte da consistência “automática” por velocidade, e precisa compensar com mecanismos de controle.
Cenários típicos em que desnormalização pode fazer sentido
- Consultas muito frequentes que exigem várias junções e têm latência sensível (ex.: tela inicial com KPIs).
- Agregações pesadas (ex.: total por dia, total por cliente) em bases grandes.
- Histórico imutável: necessidade de “congelar” dados como eram no momento do evento (ex.: nome do produto no momento da venda), desde que isso seja um requisito de negócio e não apenas conveniência.
Exemplo: armazenar total do pedido
Modelo normalizado calcula o total somando itens:
Pedido(id_pedido, data, ...) ItemPedido(id_pedido, id_produto, qtd, preco_unitario)Consulta do total:
SELECT id_pedido, SUM(qtd * preco_unitario) AS total FROM ItemPedido GROUP BY id_pedido;Em alto volume, isso pode ser caro. Desnormalização possível:
Pedido(id_pedido, data, total_pedido, ...)Risco: total_pedido ficar diferente da soma dos itens.
Mitigações recomendadas
1) Regras de consistência no banco (triggers/constraints)
Manter total_pedido atualizado automaticamente quando itens mudarem. Exemplo conceitual (pseudocódigo):
Ao inserir/atualizar/remover ItemPedido: recalcular total do Pedido correspondente.Se o SGBD suportar, prefira regras declarativas e constraints; quando não for possível, use trigger com cuidado (impacto em escrita e risco de deadlocks).
2) Colunas geradas/materializadas (quando disponível)
Alguns bancos suportam colunas computadas ou visões materializadas para acelerar leitura mantendo governança de atualização. A ideia é não “inventar” um valor manualmente, e sim derivá-lo de forma controlada.
3) Índices para reduzir a necessidade de desnormalizar
Muitas vezes, o problema é falta de índice, não de normalização. Antes de duplicar dados, avalie:
- Índice em
ItemPedido(id_pedido)para acelerar agregação por pedido. - Índices compostos alinhados ao filtro/ordenação das consultas críticas.
Exemplo:
CREATE INDEX idx_itempedido_pedido ON ItemPedido(id_pedido);4) Estratégia de “snapshot” explícito para dados históricos
Se o requisito for histórico (ex.: “o nome do produto na nota deve permanecer como na data da compra”), a duplicação pode ser correta, mas precisa ser intencional e documentada:
- Armazenar
nome_produto_snapshotno item. - Definir regra: o snapshot é preenchido no momento da venda e nunca mais é atualizado.
Isso evita o erro comum de duplicar nome_produto “por comodidade” e depois tentar mantê-lo sincronizado para sempre.
5) Auditoria e validações periódicas
Quando há desnormalização, inclua rotinas de verificação:
- Job que compara
Pedido.total_pedidocom a soma dos itens e registra divergências. - Relatórios de integridade para detectar drift.
Resumo aplicado das formas normais com um exemplo encadeado
Considere esta tabela inicial (mistura de pedido, cliente e produto):
Venda(id_venda, data, id_cliente, nome_cliente, telefones, id_produto, nome_produto, categoria, qtd, cep, cidade, uf)- 1FN: separar
telefonesem tabela própria (um telefone por linha). - 2FN: se houver chave composta para itens (ex.:
(id_venda, id_produto)), remover atributos que dependem só deid_produto(nome/categoria) paraProduto. - 3FN: remover
cidade/ufse dependem decep, criando tabelaCEP.
O resultado é um conjunto de tabelas onde cada fato tem “um lugar certo”, reduzindo anomalias e facilitando evolução do modelo.