Normalization has a reputation as a boring subject full of memorization — 1NF, 2NF, 3NF, forms nobody remembers on the spot. But the idea behind it is simple and practical: organize data so that each fact is stored in a single place. Once you understand the problem each form solves, you don't need to memorize anything.
The problem: duplicated data causes anomalies
Imagine a single table recording orders with the customer's name and phone repeated on every row. Three things go wrong:
- Update anomaly — the customer changes phone number and you have to change dozens of rows; miss one and the data becomes inconsistent.
- Insertion anomaly — you can't register a new customer unless they've placed an order.
- Deletion anomaly — deleting a customer's last order deletes the customer along with it.
To normalize is to eliminate these redundancies by splitting the table into smaller, well-defined ones.
1NF: atomic values
The First Normal Form requires each cell to hold a single value. No telefones column with "1199, 1188" separated by commas, and no repeated columns like produto1, produto2. If you have a list inside a cell, it becomes rows in its own table.
2NF: dependence on the whole key
The Second Normal Form only matters when the primary key is composite (two or more columns). It says: every non-key attribute must depend on the whole key, not on part of it.
In an order-items table with key (pedido_id, produto_id), nome_produto depends only on produto_id — part of the key. That violates 2NF. The fix is to move the name to a products table.
3NF: no transitive dependency
The Third Normal Form tackles transitive dependency: a non-key attribute that depends on another non-key attribute. If in the customers table you store cidade (city) and estado (state), and the state is determined by the city, there's a transitive dependency. City becomes its own table.
An evolving example
See how a messy table gets transformed:
| Before (everything together) | After (3NF) |
|---|---|
pedidos: id, cliente_nome, cliente_tel, produtos (list), produto_preco | clientes: id, nome, tel |
| Customer and product data repeated on every row | produtos: id, nome, preco |
| Anomalies on every update | itens_pedido: pedido_id, produto_id, qtd |
Each fact — the customer's phone, the product's price — now lives in exactly one place.
Don't memorize "2NF is partial dependency". Ask: "is this data repeated for no reason? If I change it, will I have to change it in several places?" If so, it still needs normalizing.
When to denormalize on purpose
Normalization is great for writes and integrity, but it can be costly on reads: to build a report, you join many tables with JOINs. In read-heavy scenarios, it's sometimes worth denormalizing on purpose — duplicating data in a controlled way to avoid joins and gain speed. It's a conscious trade-off decision, not laziness. Analytical models, as we'll see in dimensional modeling, embrace this idea.
Conclusion
The first three normal forms solve, one by one, specific sources of redundancy: non-atomic values, partial dependency and transitive dependency. Understand the problem behind each one and you normalize by reasoning. In interviews it's common to be asked which form a table violates — and day to day, knowing when to keep the schema normalized and when to relax it on purpose is what distinguishes a good data design.