Normalização na Modelagem de Dados: redução de redundância sem perder significado

Capítulo 10

Tempo estimado de leitura: 10 minutos

+ Exercício

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:

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

  • id_produto -> nome
  • id_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_unitario
  • id_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 -> cep
  • cep -> cidade
  • cep -> 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 -> B onde 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_cliente em Pedido, ItemPedido, Fatura).
  • Campos derivados persistidos sem regra (ex.: total_pedido armazenado sem controle de consistência).
  • Dependências escondidas: um atributo que “parece” do registro, mas na verdade depende de outro (ex.: uf depende de cep).
  • 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_snapshot no 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_pedido com 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 telefones em tabela própria (um telefone por linha).
  • 2FN: se houver chave composta para itens (ex.: (id_venda, id_produto)), remover atributos que dependem só de id_produto (nome/categoria) para Produto.
  • 3FN: remover cidade/uf se dependem de cep, criando tabela CEP.

O resultado é um conjunto de tabelas onde cada fato tem “um lugar certo”, reduzindo anomalias e facilitando evolução do modelo.

Agora responda o exercício sobre o conteúdo:

Ao avaliar a tabela ItemPedido(id_pedido, id_produto, nome_produto, categoria, qtd, preco_unitario), cuja chave do item é (id_pedido, id_produto), qual ajuste melhor atende à 2FN e por quê?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

A 2FN elimina dependências parciais em chaves compostas. Como id_produto determina nome_produto e categoria, esses atributos não dependem da chave inteira (id_pedido, id_produto) e devem ir para Produto.

Próximo capitúlo

Passo a passo de Modelagem de Dados: do texto ao Diagrama ER (conceitual)

Arrow Right Icon
Capa do Ebook gratuito Modelagem de Dados do Zero: Entidades, Relacionamentos e Regras de Negócio
63%

Modelagem de Dados do Zero: Entidades, Relacionamentos e Regras de Negócio

Novo curso

16 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.