Um guia definitivo para entender de verdade o que acontece quando você projeta um banco de dados do jeito certo.
Por que isso importa?
Você já se deparou com uma tabela de banco de dados que parecia uma bagunça? Colunas repetidas, dados duplicados em vários lugares, e quando você atualizava uma linha, precisava atualizar dezenas de outras para manter tudo consistente? Isso tem nome: anomalias de banco de dados.
A normalização é o processo de organizar um banco de dados relacional para reduzir redundâncias e dependências problemáticas. Quando feita corretamente, ela:
- Elimina dados duplicados
- Garante consistência dos dados
- Facilita manutenção e atualizações
- Reduz anomalias de inserção, atualização e exclusão
Mas antes de mergulhar nas formas normais, precisamos entender de onde elas vieram.
A Origem: Edgar Codd e o Modelo Relacional
Em 1970, o cientista da computação britânico Edgar F. Codd, trabalhando na IBM, publicou o artigo seminal "A Relational Model of Data for Large Shared Data Banks". Esse artigo fundou as bases teóricas dos bancos de dados relacionais que usamos até hoje.
Codd percebeu que os bancos de dados da época (hierárquicos e em rede) tinham dependências físicas de implementação que tornavam a manutenção um pesadelo. Ele propôs um modelo matemático baseado na teoria dos conjuntos e na lógica de predicados.
As formas normais foram introduzidas por ele progressivamente:
- 1FN → introduzida no artigo de 1970
- 2FN e 3FN → formalizadas em 1971
- BCNF → proposta junto com Raymond Boyce em 1974
- 4FN → proposta por Ronald Fagin em 1977
- 5FN → também formalizada por Fagin em 1979
Cada forma normal é um nível mais rigoroso do anterior. Para estar na 3FN, por exemplo, a tabela precisa estar na 1FN e na 2FN antes.
Conceitos Fundamentais Antes de Começar
Antes de falar sobre as formas normais, precisamos ter alguns conceitos na ponta da língua.
Chave Primária (Primary Key)
Um atributo (ou conjunto de atributos) que identifica unicamente cada linha de uma tabela.
Chave Candidata (Candidate Key)
Qualquer atributo (ou conjunto mínimo de atributos) que poderia servir como chave primária. Uma tabela pode ter várias chaves candidatas; escolhemos uma para ser a primária.
Chave Composta
Uma chave primária formada por dois ou mais atributos combinados.
Dependência Funcional
Dizemos que o atributo B tem dependência funcional de A (escrito A → B) quando para cada valor de A existe exatamente um valor de B.
Exemplo: CPF → Nome — para cada CPF existe um único nome associado.
Dependência Parcial
Ocorre em tabelas com chave composta quando um atributo não-chave depende apenas de parte da chave, e não da chave inteira.
Dependência Transitiva
Quando A → B e B → C, então A → C de forma indireta, passando por B.
Dependência Multivalorada
Quando um atributo pode ter múltiplos valores independentes associados a uma chave. Veremos isso na 4FN.
O Banco de Dados de Exemplo
Para tornar tudo concreto, vamos trabalhar com um sistema de uma escola. Começaremos com uma tabela desnormalizada e iremos normalizando passo a passo.
Tabela inicial: MATRICULA
| aluno_id | aluno_nome | aluno_emails | curso_id | curso_nome | professor_id | professor_nome | sala | turno | nota |
|---|---|---|---|---|---|---|---|---|---|
| 1 | Ana Lima | ana@mail.com, ana@uni.br | BD01 | Banco de Dados | P10 | Carlos Souza | 101 | Manhã | 8.5 |
| 1 | Ana Lima | ana@mail.com, ana@uni.br | PY01 | Python Avançado | P20 | Maria Santos | 202 | Tarde | 9.0 |
| 2 | Bruno Dias | bruno@mail.com | BD01 | Banco de Dados | P10 | Carlos Souza | 101 | Manhã | 7.0 |
| 3 | Carla Vaz | carla@mail.com | BD01 | Banco de Dados | P10 | Carlos Souza | 101 | Manhã | 9.5 |
| 3 | Carla Vaz | carla@mail.com | PY01 | Python Avançado | P20 | Maria Santos | 202 | Tarde | 8.0 |
Olhando essa tabela, já identificamos vários problemas:
-
aluno_emailscontém múltiplos valores em uma única célula -
aluno_nomese repete para cada matrícula do mesmo aluno -
curso_nome,professor_id,professor_nome,salaeturnose repetem para cada aluno no mesmo curso
Agora, vamos normalizar!
1ª Forma Normal (1FN) — Atomicidade
O que é?
Uma tabela está na Primeira Forma Normal quando:
- Todos os atributos contêm valores atômicos (indivisíveis, não listas ou conjuntos)
- Cada coluna guarda apenas um tipo de informação
- Cada linha é única (existe uma chave primária)
- A ordem das linhas não importa para o significado dos dados
A regra mais importante da 1FN é a da atomicidade: uma célula deve conter um único valor, nunca uma lista.
O Problema
Na nossa tabela, a coluna aluno_emails viola a 1FN porque armazena múltiplos emails numa única célula:
aluno_emails = "ana@mail.com, ana@uni.br"
Isso causa problemas:
- Como filtrar todos os alunos com email
@uni.br? - Como remover apenas um dos emails?
- Como garantir que os emails são válidos?
A Solução
Separamos os dados em linhas distintas e garantimos que cada célula tem um único valor.
Tabela: MATRICULA (1FN)
| aluno_id | aluno_nome | aluno_email | curso_id | curso_nome | professor_id | professor_nome | sala | turno | nota |
|---|---|---|---|---|---|---|---|---|---|
| 1 | Ana Lima | ana@mail.com | BD01 | Banco de Dados | P10 | Carlos Souza | 101 | Manhã | 8.5 |
| 1 | Ana Lima | ana@uni.br | BD01 | Banco de Dados | P10 | Carlos Souza | 101 | Manhã | 8.5 |
| 1 | Ana Lima | ana@mail.com | PY01 | Python Avançado | P20 | Maria Santos | 202 | Tarde | 9.0 |
| 1 | Ana Lima | ana@uni.br | PY01 | Python Avançado | P20 | Maria Santos | 202 | Tarde | 9.0 |
| 2 | Bruno Dias | bruno@mail.com | BD01 | Banco de Dados | P10 | Carlos Souza | 101 | Manhã | 7.0 |
| 3 | Carla Vaz | carla@mail.com | BD01 | Banco de Dados | P10 | Carlos Souza | 101 | Manhã | 9.5 |
| 3 | Carla Vaz | carla@mail.com | PY01 | Python Avançado | P20 | Maria Santos | 202 | Tarde | 8.0 |
Agora todos os valores são atômicos. Mas repare que criamos ainda mais redundância: os dados do curso de Ana aparecem duplicados por causa dos dois emails. A chave composta aqui seria (aluno_id, aluno_email, curso_id).
Nota prática: Na prática, emails múltiplos seriam tratados em uma tabela separada desde o design inicial. Usamos esse exemplo apenas para ilustrar a violação de atomicidade.
Resumindo a 1FN
| ✅ Correto (1FN) | ❌ Violação (1FN) |
|---|---|
| Uma célula = um valor | Uma célula = lista de valores |
email = "ana@mail.com" |
email = "ana@mail.com, ana@uni.br" |
telefone = "99999-0000" |
telefones = "99999-0000 / 88888-1111" |
2ª Forma Normal (2FN) — Sem Dependências Parciais
O que é?
Uma tabela está na Segunda Forma Normal quando:
- Já está na 1FN
- Todos os atributos não-chave dependem da chave primária inteira, não apenas de parte dela
A 2FN só se aplica quando existe uma chave composta. Se a chave primária é um único atributo, a tabela já satisfaz automaticamente a 2FN (desde que esteja na 1FN).
O Problema
Vamos simplificar nossa tabela e focar na estrutura principal. Considere que na tabela MATRICULA, a chave primária composta é (aluno_id, curso_id):
| aluno_id | curso_id | aluno_nome | curso_nome | professor_id | professor_nome | sala | turno | nota |
|---|---|---|---|---|---|---|---|---|
| 1 | BD01 | Ana Lima | Banco de Dados | P10 | Carlos Souza | 101 | Manhã | 8.5 |
| 1 | PY01 | Ana Lima | Python Avançado | P20 | Maria Santos | 202 | Tarde | 9.0 |
| 2 | BD01 | Bruno Dias | Banco de Dados | P10 | Carlos Souza | 101 | Manhã | 7.0 |
| 3 | BD01 | Carla Vaz | Banco de Dados | P10 | Carlos Souza | 101 | Manhã | 9.5 |
| 3 | PY01 | Carla Vaz | Python Avançado | P20 | Maria Santos | 202 | Tarde | 8.0 |
Analisando as dependências:
-
aluno_nomedepende apenas dealuno_id→ dependência parcial ❌ -
curso_nome,professor_id,professor_nome,sala,turnodependem apenas decurso_id→ dependência parcial ❌ -
notadepende de(aluno_id, curso_id)juntos → dependência total ✅
aluno_nome não muda dependendo do curso que o aluno faz. Depende só do aluno. Isso é uma dependência parcial e viola a 2FN.
Anomalias causadas pela violação da 2FN
Anomalia de atualização: Se o nome da Ana mudar, precisamos atualizar em todas as linhas onde ela aparece. Se esquecermos uma, teremos inconsistência.
Anomalia de inserção: Não conseguimos cadastrar um novo curso sem ter pelo menos um aluno matriculado.
Anomalia de exclusão: Se o Bruno cancelar a matrícula em BD01, perdemos o registro de que o curso BD01 existe.
A Solução
Decompomos em tabelas separadas, cada uma com atributos que dependem totalmente de sua chave.
Tabela: ALUNO
| aluno_id (PK) | aluno_nome |
|---|---|
| 1 | Ana Lima |
| 2 | Bruno Dias |
| 3 | Carla Vaz |
Tabela: CURSO
| curso_id (PK) | curso_nome | professor_id | professor_nome | sala | turno |
|---|---|---|---|---|---|
| BD01 | Banco de Dados | P10 | Carlos Souza | 101 | Manhã |
| PY01 | Python Avançado | P20 | Maria Santos | 202 | Tarde |
Tabela: MATRICULA
| aluno_id (FK) | curso_id (FK) | nota |
|---|---|---|
| 1 | BD01 | 8.5 |
| 1 | PY01 | 9.0 |
| 2 | BD01 | 7.0 |
| 3 | BD01 | 9.5 |
| 3 | PY01 | 8.0 |
Agora cada atributo depende integralmente de sua chave primária. As anomalias foram eliminadas.
Resumindo a 2FN
| Atributo | Depende de | Situação |
|---|---|---|
aluno_nome |
aluno_id (parte da PK) |
❌ Parcial |
curso_nome |
curso_id (parte da PK) |
❌ Parcial |
nota |
(aluno_id, curso_id) (PK inteira) |
✅ Total |
3ª Forma Normal (3FN) — Sem Dependências Transitivas
O que é?
Uma tabela está na Terceira Forma Normal quando:
- Já está na 2FN
- Nenhum atributo não-chave depende de outro atributo não-chave (sem dependências transitivas)
Em outras palavras: atributos não-chave devem depender diretamente da chave primária, e não através de outro atributo intermediário.
O Problema
Olhe a tabela CURSO que criamos:
| curso_id | curso_nome | professor_id | professor_nome | sala | turno |
|---|---|---|---|---|---|
| BD01 | Banco de Dados | P10 | Carlos Souza | 101 | Manhã |
| PY01 | Python Avançado | P20 | Maria Santos | 202 | Tarde |
Analisando as dependências:
curso_id → professor_id (direto, ok)
curso_id → professor_nome (transitivo! via professor_id)
professor_id → professor_nome
professor_nome não depende diretamente de curso_id. Ele depende de professor_id, que por sua vez depende de curso_id. Isso é uma dependência transitiva.
Da mesma forma:
curso_id → sala (direto)
curso_id → turno (direto, mas…)
Se um curso mudar de sala, precisamos atualizar apenas uma linha. Mas se um professor mudar de nome, precisamos atualizar todas as ocorrências do professor nessa tabela.
Anomalias causadas
Se Carlos Souza mudar seu nome, precisamos atualizar em todos os cursos onde ele leciona. E se adicionarmos um novo professor que ainda não leciona nenhum curso? Não conseguimos — não há onde inserir.
A Solução
Extraímos o professor para sua própria tabela.
Tabela: PROFESSOR
| professor_id (PK) | professor_nome |
|---|---|
| P10 | Carlos Souza |
| P20 | Maria Santos |
Tabela: CURSO (3FN)
| curso_id (PK) | curso_nome | professor_id (FK) | sala | turno |
|---|---|---|---|---|
| BD01 | Banco de Dados | P10 | 101 | Manhã |
| PY01 | Python Avançado | P20 | 202 | Tarde |
Agora professor_nome não está mais na tabela CURSO. Buscamos via JOIN quando necessário. Cada tabela tem apenas dependências diretas à sua chave.
A Regra de Codd para a 3FN
Edgar Codd definiu formalmente a 3FN assim: "Um esquema de relação R está na 3FN se, para toda dependência funcional não-trivial X → A em R, X é uma superchave de R, ou A é um atributo primo (parte de alguma chave candidata)."
Dito de maneira simples: todo atributo não-chave deve depender da chave, da chave inteira, e de nada mais além da chave.
Resumindo a 3FN
Dependência transitiva:
curso_id → professor_id → professor_nome
↑
Isso é o problema!
Solução: extrair professor para tabela própria.
Forma Normal de Boyce-Codd (BCNF) — A 3.5FN
O que é?
A BCNF (Boyce-Codd Normal Form) foi proposta em 1974 como um refinamento mais rigoroso da 3FN. Às vezes chamada de 3.5FN, ela elimina anomalias que a 3FN ainda permite em casos específicos com múltiplas chaves candidatas sobrepostas.
A regra é simples: para toda dependência funcional não-trivial X → Y, X deve ser uma superchave.
A diferença da 3FN: a 3FN permitia que Y fosse um atributo primo (parte de uma chave candidata). A BCNF não faz essa exceção — X sempre tem que ser superchave, ponto final.
Quando a 3FN não é suficiente?
Considere um cenário onde:
- Cada disciplina pode ter vários professores
- Cada professor ensina apenas uma disciplina
- Cada aluno, para uma disciplina, tem um professor específico
Tabela: AULA
| aluno_id | disciplina | professor |
|---|---|---|
| 1 | Banco de Dados | Carlos Souza |
| 1 | Python | Maria Santos |
| 2 | Banco de Dados | Ana Costa |
| 3 | Banco de Dados | Carlos Souza |
| 3 | Python | Maria Santos |
Chaves candidatas:
-
(aluno_id, disciplina)— identifica unicamente o professor do aluno -
(aluno_id, professor)— também identifica unicamente a disciplina
Dependências funcionais:
-
(aluno_id, disciplina) → professor✅ -
(aluno_id, professor) → disciplina✅ -
professor → disciplina(cada professor ensina só uma disciplina) ✅
A tabela está na 3FN porque disciplina é um atributo primo (parte de uma chave candidata) — a 3FN permite isso. Mas há um problema: professor → disciplina tem professor como determinante, mas professor não é superchave.
Anomalia: Se Carlos Souza mudar de disciplina (de BD para Redes), precisamos atualizar múltiplas linhas. Se todas as matrículas de um professor forem canceladas, perdemos qual disciplina ele ensina.
A Solução BCNF
Decompomos para garantir que todo determinante seja superchave:
Tabela: PROFESSOR_DISCIPLINA
| professor (PK) | disciplina |
|---|---|
| Carlos Souza | Banco de Dados |
| Ana Costa | Banco de Dados |
| Maria Santos | Python |
Tabela: MATRICULA_PROFESSOR
| aluno_id | professor |
|---|---|
| 1 | Carlos Souza |
| 1 | Maria Santos |
| 2 | Ana Costa |
| 3 | Carlos Souza |
| 3 | Maria Santos |
Agora toda dependência X → Y tem X como superchave.
BCNF vs 3FN: Qual usar?
A BCNF é mais forte, mas a decomposição para BCNF pode perder dependências funcionais (elas não ficam mais representadas em uma única tabela). Em alguns casos, mantemos a 3FN intencionalmente para preservar certas restrições. A BCNF é preferida quando:
- Você pode preservar todas as dependências após a decomposição
- A integridade dos dados é crítica
- A consistência > conveniência de consulta
4ª Forma Normal (4FN) — Sem Dependências Multivaloradas
O que é?
A Quarta Forma Normal, proposta por Ronald Fagin em 1977, aborda um problema diferente das anteriores: dependências multivaloradas.
Uma tabela tem uma dependência multivalorada A ↠ B quando, para cada valor de A, existe um conjunto de valores de B que é independente de qualquer outro atributo C da tabela.
Regra: Uma tabela está na 4FN se, para toda dependência multivalorada não-trivial A ↠ B, A é uma superchave.
O Problema
Imagine que um professor pode ensinar múltiplas disciplinas e falar múltiplos idiomas, e essas informações são independentes entre si.
Tabela: PROFESSOR_HABILIDADES
| professor | disciplina | idioma |
|---|---|---|
| Carlos Souza | Banco de Dados | Português |
| Carlos Souza | Banco de Dados | Inglês |
| Carlos Souza | Python | Português |
| Carlos Souza | Python | Inglês |
| Maria Santos | Python | Português |
| Maria Santos | Python | Espanhol |
Para representar que Carlos ensina BD e Python, e fala Português e Inglês, precisamos de 4 linhas (produto cartesiano). Isso porque as disciplinas e idiomas são independentes entre si — o fato de Carlos falar Inglês não tem nada a ver com ele ensinar Python.
Dependências multivaloradas aqui:
-
professor ↠ disciplina(um professor → múltiplas disciplinas) -
professor ↠ idioma(um professor → múltiplos idiomas)
Anomalia: Se Carlos aprender um novo idioma (Espanhol), precisamos adicionar uma linha para cada disciplina que ele ensina. Se esquecermos uma, a tabela fica inconsistente.
A Solução
Separamos as dependências multivaloradas em tabelas distintas.
Tabela: PROFESSOR_DISCIPLINA
| professor | disciplina |
|---|---|
| Carlos Souza | Banco de Dados |
| Carlos Souza | Python |
| Maria Santos | Python |
Tabela: PROFESSOR_IDIOMA
| professor | idioma |
|---|---|
| Carlos Souza | Português |
| Carlos Souza | Inglês |
| Maria Santos | Português |
| Maria Santos | Espanhol |
Agora cada tabela tem uma única dependência multivalorada, e professor é superchave em ambas.
A Intuição por trás da 4FN
Se você tem múltiplos atributos multivalorados independentes em uma mesma tabela, você está criando um produto cartesiano desnecessário. A 4FN diz: separe fatos independentes em tabelas independentes.
5ª Forma Normal (5FN) — Sem Dependências de Junção
O que é?
A Quinta Forma Normal (também chamada de Forma Normal de Projeção-Junção ou PJ/NF), proposta por Fagin em 1979, é a mais sofisticada das formas normais clássicas.
Uma tabela está na 5FN quando não pode ser decomposta em tabelas menores sem perda de informação, exceto se essa decomposição for baseada em superchaves.
Em outras palavras: se uma tabela puder ser reconstruída perfeitamente a partir do JOIN de suas projeções, então ela pode ser decomposta. Se essa reconstrução introduzir linhas falsas (spurious tuples), a decomposição é inválida.
O Problema
A 5FN captura restrições de negócio complexas. Considere:
Uma empresa tem vendedores, produtos e regiões. A regra de negócio é: "Um vendedor vende um produto em uma região somente se: ele vende nessa região, ele vende esse produto, e esse produto é vendido nessa região."
Tabela: VENDEDOR_PRODUTO_REGIAO
| vendedor | produto | regiao |
|---|---|---|
| João | Notebook | Sul |
| João | Notebook | Sudeste |
| João | Tablet | Sul |
| Maria | Tablet | Sudeste |
| Maria | Notebook | Sudeste |
Se decompuséssemos em:
Tabela A: (vendedor, produto) | Tabela B: (produto, regiao) | Tabela C: (vendedor, regiao)
E depois fizéssemos o JOIN das três, poderíamos gerar combinações que não existem na realidade — chamadas de tuplas espúrias. Por exemplo, o JOIN poderia sugerir que João vende Tablet no Sudeste, o que não é verdade.
Para estar na 5FN, precisamos garantir que nenhuma dependência de junção oculta possa ser violada por uma decomposição inadequada.
Por que a 5FN é rara na prática?
Diferente das outras formas normais, a 5FN é difícil de detectar automaticamente e raramente viola-se na prática quando se chega à 4FN. Ela é mais relevante em contextos acadêmicos e em sistemas com regras de negócio altamente complexas e entrelaçadas.
Na prática, a maioria dos projetos de banco de dados se satisfaz com a 3FN ou BCNF, com atenção à 4FN quando há atributos multivalorados independentes.
Resumo Visual: A Hierarquia das Formas Normais
Tabela Bruta (sem normalização)
│
▼
┌─────────────────────────────────────────────┐
│ 1FN: Valores atômicos, sem listas em células│
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 2FN: Sem dependências parciais │
│ (todo atributo depende da chave inteira) │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 3FN: Sem dependências transitivas │
│ (atributos dependem só da chave) │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ BCNF: Todo determinante é superchave │
│ (versão mais rigorosa da 3FN) │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 4FN: Sem dependências multivaloradas │
│ independentes │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 5FN: Sem dependências de junção ocultas │
│ (decomposição sem perda) │
└─────────────────────────────────────────────┘
Tabela de Referência Rápida
| Forma Normal | Problema que resolve | Regra principal |
|---|---|---|
| 1FN | Valores não-atômicos (listas em células) | Cada célula = um único valor |
| 2FN | Dependências parciais em chaves compostas | Atributo não-chave depende da chave inteira |
| 3FN | Dependências transitivas | Atributo não-chave depende só da chave |
| BCNF | Determinantes que não são superchaves | Todo determinante é superchave |
| 4FN | Dependências multivaloradas independentes | Separe fatos multivalorados independentes |
| 5FN | Dependências de junção ocultas | Decomposição sem tuplas espúrias |
Desnormalização: Quando Voltar Atrás?
Vale mencionar que normalização não é uma regra absoluta e imutável. Em sistemas de alta performance ou data warehouses, às vezes desnormalizamos intencionalmente para:
- Reduzir o número de JOINs em queries analíticas
- Melhorar performance de leitura
- Simplificar queries complexas
Isso é comum em bancos orientados a relatórios (OLAP), onde a leitura é muito mais frequente que a escrita. O padrão Star Schema e o Snowflake Schema são exemplos de modelos intencionalmente desnormalizados.
A regra prática: normalize para sistemas transacionais (OLTP), desnormalize com cuidado para sistemas analíticos (OLAP).
Conclusão
A normalização de banco de dados é uma das habilidades mais importantes para qualquer desenvolvedor que trabalhe com dados. Ela não é um conjunto arbitrário de regras burocráticas — cada forma normal resolve problemas reais de consistência, redundância e manutenibilidade.
Recapitulando a jornada:
- 1FN nos ensina que dados devem ser atômicos, indivisíveis.
- 2FN nos ensina que partes de dados não devem esconder dependências parciais.
- 3FN nos ensina que cadeias de dependência transitiva são fontes de inconsistência.
- BCNF refina a 3FN para casos com múltiplas chaves candidatas sobrepostas.
- 4FN nos ensina a separar fatos multivalorados independentes.
- 5FN nos ensina que decomposições inadequadas podem criar informações falsas.
Na prática diária, 3FN ou BCNF é o destino na maioria dos projetos. Mas entender todas as formas normais te dá ferramental teórico para reconhecer e resolver problemas sutis de design que aparecem ao longo do tempo.
Como dizia Codd: "um modelo de dados correto é a fundação de todo sistema de informação confiável."
Gostou do artigo? Deixa uma reação ou comenta o que achou! 🚀
Top comments (0)