To normalize is to split data into tables so that each piece of data is stored only once. It’s the step between the entity-relationship model and CREATE TABLE: it decides which tables you need and which columns each one gets.

You do it by applying a set of rules called normal forms. There are several, but in practice the first three are enough for most databases.

The problem: one table that stores everything

Imagine the shop keeps its orders in a single sheet, just as they come in:

orderId customer customerAddress products descriptions prices
1 Peter Davis 3608 Sycamore Lake Road Xiaomi Mi 11 Smartphone 286.95
2 Mavin Pettitt 2336 Cottonwood Lane Play Station 5, Xbox series X Sony Game Console, Xbox Game Console 549.95, 499.99
3 Helen Ward 711 Hershell Hollow Road Echo DOT 3, Echo DOT 4 Alexa Speaker, Alexa Speaker 39.99, 59.99

It works while it’s small, but it has three problems, called anomalies:

  • Update anomaly. If Mavin moves, the address has to be fixed on every order. Miss one and the database holds two addresses for the same person.
  • Insertion anomaly. You can’t add a customer who hasn’t bought anything yet, because each row is an order.
  • Deletion anomaly. If you delete Peter’s only order, you also lose everything you knew about Peter.

On top of that, with several products in one cell there’s no simple way to answer “how many Xboxes have we sold?”.

First normal form (1NF): one value per cell

A table is in 1NF if:

  • Each cell holds a single value (no comma-separated lists).
  • There are no repeating groups of columns (product1, product2, product3…).
  • Each row can be identified by a key.

We split the products into rows. The key becomes the combination of order and product:

orderId product customer customerAddress description price
1 Xiaomi Mi 11 Peter Davis 3608 Sycamore Lake Road Smartphone 286.95
2 Play Station 5 Mavin Pettitt 2336 Cottonwood Lane Sony Game Console 549.95
2 Xbox series X Mavin Pettitt 2336 Cottonwood Lane Xbox Game Console 499.99
3 Echo DOT 3 Helen Ward 711 Hershell Hollow Road Alexa Speaker 39.99
3 Echo DOT 4 Helen Ward 711 Hershell Hollow Road Alexa Speaker 59.99

Now you can count and sum per product, but Mavin and her address are repeated.

Second normal form (2NF): everything depends on the whole key

A table is in 2NF if it’s in 1NF and every column depends on the whole key, not just part of it. It only affects tables whose key has several columns.

Our key is (orderId, product). But:

  • customer and customerAddress depend only on orderId.
  • description and price depend only on product.

Each group moves to its own table, and the original keeps only what depends on both columns together (in a real shop, the quantity of each product in each order):

Orders

orderId customer customerAddress
1 Peter Davis 3608 Sycamore Lake Road
2 Mavin Pettitt 2336 Cottonwood Lane
3 Helen Ward 711 Hershell Hollow Road

Products

product description price
Xiaomi Mi 11 Smartphone 286.95
Play Station 5 Sony Game Console 549.95
Xbox series X Xbox Game Console 499.99
Echo DOT 3 Alexa Speaker 39.99
Echo DOT 4 Alexa Speaker 59.99

OrderLines

orderId product quantity
1 Xiaomi Mi 11 15
2 Play Station 5 4
2 Xbox series X 2
3 Echo DOT 3 50
3 Echo DOT 4 50

Each product’s price now lives in one place.

Third normal form (3NF): nothing depends on a non-key column

A table is in 3NF if it’s in 2NF and no column depends on another column that isn’t the key (a so-called transitive dependency).

In Orders, the address doesn’t depend on the order but on the customer: if Mavin places ten orders, her address is repeated ten times. The fix is the same as before: move the customer to its own table and keep only a reference to it in Orders.

Customers

customerId name address
1 Peter Davis 3608 Sycamore Lake Road
2 Mavin Pettitt 2336 Cottonwood Lane
3 Helen Ward 711 Hershell Hollow Road

Orders

orderId customerId
1 1
2 2
3 3

customerId in Orders is a Foreign Key: the link to the Customers table. All three anomalies are gone. The address is changed in one place, you can add a customer with no orders, and deleting an order doesn’t delete the customer.

The rule to remember

A classic way to sum up the three normal forms: every column must depend on the key, the whole key and nothing but the key.

  • “On the key”: 1NF, each row is identified by a key and each cell holds one value.
  • “The whole key”: 2NF.
  • “And nothing but the key”: 3NF.

Should you always normalize?

For an application database, where data is written constantly (orders, users, invoices), yes: normalizing to 3NF prevents inconsistencies and is the standard starting point.

There are cases where you denormalize on purpose, such as data warehouses and reporting tables, where almost everything is reads and repeating data saves a lot of JOINs. But that’s a decision you make on top of a normalized design, not an excuse to skip it.

The course’s Customers and Orders tables are almost normalized. The exception is totalPrice, which is calculated from two other columns. It’s a computed column that SQL Server maintains itself, so it can’t get out of date. And since each order has a single product, there’s no need for an order lines table.

Practice

  1. A Students (studentId, name, subjects) table stores values like 'Maths, Physics' in subjects. Which normal form does it break and how would you fix it?
  2. In an Enrollments (studentId, subjectId, subjectName, grade) table with key (studentId, subjectId), which column breaks 2NF?
  3. In Employees (employeeId, name, departmentId, departmentName), which column breaks 3NF?