DEV Community

Cover image for Normalização de Banco de Dados: Das Origens às 5 Formas Normais (+ BCNF) com Exemplos Práticos
Felipe Cezar
Felipe Cezar

Posted on

Normalização de Banco de Dados: Das Origens às 5 Formas Normais (+ BCNF) com Exemplos Práticos

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_emails contém múltiplos valores em uma única célula
  • aluno_nome se repete para cada matrícula do mesmo aluno
  • curso_nome, professor_id, professor_nome, sala e turno se 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:

  1. Todos os atributos contêm valores atômicos (indivisíveis, não listas ou conjuntos)
  2. Cada coluna guarda apenas um tipo de informação
  3. Cada linha é única (existe uma chave primária)
  4. 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"
Enter fullscreen mode Exit fullscreen mode

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:

  1. Já está na 1FN
  2. 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_nome depende apenas de aluno_id → dependência parcial
  • curso_nome, professor_id, professor_nome, sala, turno dependem apenas de curso_id → dependência parcial
  • nota depende 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:

  1. Já está na 2FN
  2. 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
Enter fullscreen mode Exit fullscreen mode

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…)
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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)                   │
   └─────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

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 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)