DEV Community

Yuri Peixinho
Yuri Peixinho

Posted on

SQL: Normalização e Formas Normais

Introdução

Imagine uma planilha onde cada linha de um pedido guarda também o nome do cliente, o email, a cidade, o nome do produto e a categoria — tudo junto. Funciona no começo. Mas conforme o sistema cresce, começam a aparecer problemas silenciosos: dados duplicados, inconsistências difíceis de rastrear, operações que quebram coisas inesperadas. A normalização existe para eliminar esses problemas sistematicamente.

Proposta por Codd nos anos 1970 e formalizada ao longo das décadas seguintes, a normalização é um processo de reorganização do esquema em formas normais progressivas — cada uma corrigindo uma classe específica de problema.

O problema: Anomalias

Antes de ver as soluções, vale entender o que está sendo resolvido. Um esquema mal estruturado produz três tipos de anomalia.

Considere esta tabela que mistura informações de pedidos, clientes e produtos:

| pedido_id | cliente    | cliente_cidade | produto     | categoria    | qtd | preco   |
|-----------|------------|----------------|-------------|--------------|-----|---------|
| 1         | Ana Lima   | São Paulo      | Notebook    | Eletrônicos  | 1   | 3500.00 |
| 1         | Ana Lima   | São Paulo      | Mouse       | Periféricos  | 2   |   80.00 |
| 2         | Bruno Melo | Curitiba       | Notebook    | Eletrônicos  | 1   | 3500.00 |
Enter fullscreen mode Exit fullscreen mode

Anomalia de atualização: se Ana Lima mudar de cidade, é preciso atualizar todas as linhas onde ela aparece. Esquecer uma cria inconsistência — o mesmo cliente com duas cidades diferentes no banco.

Anomalia de inserção: não é possível cadastrar um produto sem vinculá-lo a um pedido. O produto não existe de forma independente nesse esquema.

Anomalia de exclusão: se o pedido 2 for deletado, os dados do Notebook somem junto — mesmo que o produto exista em outros contextos.

Esses são os sintomas de um esquema não normalizado. As formas normais são o tratamento.

Primeira Forma Normal — 1FN

Regra: cada coluna deve conter um valor atômico — indivisível. Não pode haver listas, conjuntos ou grupos repetidos dentro de uma célula.

Um exemplo que viola 1FN:

| pedido_id | cliente  | produtos                    |
|-----------|----------|-----------------------------|
| 1         | Ana Lima | Notebook, Mouse, Teclado    |
Enter fullscreen mode Exit fullscreen mode

A coluna produtos guarda múltiplos valores numa célula só. Isso parece conveniente, mas impossibilita filtrar pedidos que contenham um produto específico sem recorrer a gambiarras com LIKE.

A correção é expandir cada valor em sua própria linha:

| pedido_id | cliente  | produto  |
|-----------|----------|----------|
| 1         | Ana Lima | Notebook |
| 1         | Ana Lima | Mouse    |
| 1         | Ana Lima | Teclado  |
Enter fullscreen mode Exit fullscreen mode

Além de valores atômicos, 1FN exige que cada linha seja única — o que leva naturalmente à definição de uma chave primária.

Segunda Forma Normal — 2FN

Regra: estar em 1FN e não ter dependências parciais — todo atributo não-chave deve depender da chave primária inteira, não de apenas parte dela.

Essa forma só é relevante quando a chave primária é composta. Considere:

| pedido_id | produto_id | produto_nome | cliente    | quantidade |
|-----------|------------|--------------|------------|------------|
| 1         | 10         | Notebook     | Ana Lima   | 1          |
| 1         | 11         | Mouse        | Ana Lima   | 2          |
| 2         | 10         | Notebook     | Bruno Melo | 1          |
Enter fullscreen mode Exit fullscreen mode

A chave primária é (pedido_id, produto_id). Mas produto_nome depende apenas de produto_id — não precisa do pedido_id para ser determinado. E cliente depende apenas de pedido_id. Essas são dependências parciais.

A solução é separar em tabelas distintas, onde cada atributo depende da chave inteira da sua tabela:

-- Cada tabela tem sua própria identidade
CREATE TABLE produtos (
  idINT PRIMARY KEY,
  nomeVARCHAR(100)
);

CREATE TABLE pedidos (
  idINT PRIMARY KEY,
  cliente_idINT REFERENCES clientes(id)
);

CREATE TABLE itens_pedido (
  pedido_idINT REFERENCES pedidos(id),
  produto_idINT REFERENCES produtos(id),
  quantidadeINT,
  PRIMARY KEY (pedido_id, produto_id)
);
Enter fullscreen mode Exit fullscreen mode

Agora produto_nome vive em produtoscliente vive em pedidos, e itens_pedido guarda apenas o que é realmente da relação entre pedido e produto.

Terceira Forma Normal — 3FN

Regra: estar em 2FN e não ter dependências transitivas — nenhum atributo não-chave pode depender de outro atributo não-chave.

Exemplo:

| produto_id | nome     | categoria_id | categoria_nome |
|------------|----------|--------------|----------------|
| 10         | Notebook | 1            | Eletrônicos    |
| 11         | Mouse    | 2            | Periféricos    |
Enter fullscreen mode Exit fullscreen mode

Aqui, categoria_nome não depende de produto_id diretamente — depende de categoria_id, que por sua vez depende de produto_id. Isso é uma dependência transitiva: produto_id → categoria_id → categoria_nome.

O problema: se o nome da categoria "Eletrônicos" precisar mudar, será necessário atualizar todos os produtos dessa categoria. E se não houver nenhum produto numa categoria, ela não pode ser armazenada.

A solução, novamente, é separar:

CREATE TABLE categorias (
  idINT PRIMARY KEY,
  nomeVARCHAR(100)
);

CREATE TABLE produtos (
  idINT PRIMARY KEY,
  nomeVARCHAR(100),
  categoria_idINT REFERENCES categorias(id)
);
Enter fullscreen mode Exit fullscreen mode

Agora categoria_nome vive onde pertence, e os produtos referenciam a categoria por chave estrangeira.

Forma Normal de Boyce-Codd — BCNF

A BCNF é uma versão mais rigorosa da 3FN, proposta em 1974 por Raymond Boyce e o próprio Codd. A diferença é sutil mas importante: na 3FN, há exceções para dependências envolvendo chaves candidatas. A BCNF elimina essas exceções.

Regra: para cada dependência funcional X → Y, X deve ser uma chave candidata.

Na prática, a maioria dos esquemas bem projetados que atingem 3FN já satisfaz a BCNF. Os casos que violam BCNF mas não 3FN são relativamente raros — envolvem tabelas com múltiplas chaves candidatas sobrepostas.

4FN e 5FN

Existem formas normais além da BCNF, que tratam de casos ainda mais específicos. A 4FN lida com dependências multivaloradas — quando um atributo determina múltiplos valores independentes de outro atributo. A 5FN trata de dependências de junção. Na prática do desenvolvimento cotidiano, chegar à 3FN ou BCNF já resolve a grande maioria dos problemas. A 4FN e 5FN aparecem em modelagens muito específicas ou acadêmicas.

Desnormalização

Normalizar é bom, mas normalizar demais pode ser um problema de desempenho. Uma consulta que precisa juntar seis tabelas para montar um relatório vai ser mais lenta do que uma que lê uma tabela única já consolidada.

Desnormalização é a decisão deliberada de violar uma forma normal em prol de desempenho. É comum em sistemas de analytics e data warehouses, onde leituras são muito mais frequentes que escritas, e a redundância de dados é um custo aceitável em troca de consultas mais rápidas.

A diferença entre um banco mal projetado e um banco desnormalizado intencionalmente é exatamente essa: consciência. Desnormalizar sem entender as formas normais é acidente. Desnormalizar conhecendo os trade-offs é arquitetura.

Top comments (0)