Skip to content
PHP2Shell: The PHP Bug
Akshay Thakare

Normalization in Plain English: 1NF, 2NF, 3NF

What each normal form removes, why OLTP schemas are normalized, and why analytical models deliberately denormalize.

Data ModelingBeginner2 min readUpdated 11 Oct 2026

The goal

Normalization stores each fact once, so an update happens in one place and the data cannot contradict itself.

Start with a messy table

order_idcustomercustomer_cityproducts
O1AshaPuneRice, Milk

1NF — one value per cell

Split repeating values into rows:

order_idcustomercustomer_cityproduct
O1AshaPuneRice
O1AshaPuneMilk

2NF — every column depends on the whole key

With key (order_id, product), customer depends only on order_id. Move it out:

Plain text.txt
orders(order_id, customer)
order_lines(order_id, product)

3NF — no column depends on another non-key column

customer_city depends on the customer, not on the order:

Plain text.txt
customers(customer_id, name, city)
orders(order_id, customer_id)
order_lines(order_id, line_no, product_id, qty)

Now a customer moving city changes one row.

Why warehouses denormalize again

Analytical queries would need many joins on a 3NF schema. A star schema flattens descriptive attributes into wide dimensions (city, region and country all in dim_customer). That trades some redundancy for simple, fast queries.

Finished this note?

Share