Look at this innocent-looking table a junior developer built for course enrollments — one flat table, everything in one place:
| student_id | student_name | course_id | course_name | instructor |
|---|---|---|---|---|
| 1 | Ravi | C101 | DBMS | Prof. Rao |
| 2 | Priya | C101 | DBMS | Prof. Rao |
| 3 | Aman | C102 | OS | Prof. Iyer |
| 1 | Ravi | C102 | OS | Prof. Iyer |
Now watch it break. Prof. Rao leaves and DBMS gets a new instructor — you must update every row of that course; miss one and the table disagrees with itself. A new course with zero students? Nowhere to store it — every row needs a student. Aman drops OS? You just deleted the only evidence that Prof. Iyer teaches it. Three failures, one cause: the same fact is stored in many places. Normalization is the cure — and 1NF, 2NF, 3NF, BCNF are just increasingly strict dosages.
The three anomalies — name the disease first
Everything you just watched has a formal name. Update anomaly: one fact stored N times needs N updates (the instructor change). Insert anomaly: you can't record a fact because it must ride along with an unrelated one (new course needs a student). Delete anomaly: removing one fact accidentally destroys another (dropping the last enrollment erases the course's instructor). Interviews often ask for these three with examples — you now have all three from one table.
Functional dependency — the arrow that decides everything
Write X → Y when knowing X tells you Y: course_id → course_name (know the course, know its name), student_id → student_name. All of normalization is one question asked repeatedly: does every column depend on the key, the whole key, and nothing but the key? Each normal form checks one way that promise can break.
1NF — one value per cell
A table is in 1NF when every cell holds a single atomic value: no comma-packed lists ('9876543210,9123456789' in a phone column), no repeating column groups (phone1, phone2, phone3), and each row identifiable by a key. Why care? Because the moment data hides inside a cell, SQL goes blind — WHERE phone_number = '9876...' becomes string surgery with LIKE. The fix is always the same and you'll write it in the practice: the repeating thing gets its own table, one row per value.
2NF — the whole key
Now for tables with composite keys. Take order_items(order_id, product_id, quantity, product_name) with key (order_id, product_id). Ask each non-key column: which part of the key do you depend on? quantity needs both parts — fine. But product_name depends on product_id alone — a partial dependency, and 2NF forbids it. Consequence you can see: 'Notebook' is re-stored on every order line that contains a notebook. Fix: product_name moves to a products table where product_id is the whole key. (A 1NF table with a single-column key is automatically 2NF — no key parts to partially depend on.)
3NF — nothing but the key
students(student_id, name, dept_id, dept_name): dept_name doesn't depend on student_id directly — it depends on dept_id, which depends on student_id. A dependency through another non-key column is a transitive dependency, and 3NF forbids it. The smell is always the same: columns describing some other entity hitching a ride. Departments deserve their own table; students keep only the dept_id foreign key. The folk summary is worth memorising aloud: every non-key column depends on the key (1NF), the whole key (2NF), and nothing but the key (3NF) — "so help me Codd".
BCNF — 3NF with the loophole closed
3NF quietly tolerates one edge case: a dependency X → Y where X isn't a candidate key, as long as Y is part of some candidate key. BCNF closes it: if anything determines anything, the determinant must be a candidate key. Classic scenario: a (student, subject) → teacher table where each teacher teaches only one subject — teacher → subject holds, teacher isn't a key, yet the table passes 3NF and fails BCNF. Every BCNF table is 3NF; the reverse isn't true. For interviews: know the definition and one such example; for real projects: 3NF is the everyday standard.
Denormalization — breaking the rules on purpose
Here's the twist ending: professionals sometimes deliberately add redundancy back. A read-heavy analytics dashboard joining five normalized tables per page-load is slow; pre-joining or duplicating a column (customer_name on every order row) makes reads cheap. The price is exactly the disease we cured — update anomalies return, and consistency now depends on application discipline instead of the schema. The practice zone includes a lovely forensic exercise: hunting a city-name typo that denormalization allowed into production. The engineering answer interviewers want: normalize by default, denormalize consciously, for measured read-heavy hotspots.
Common mistakes
- Saying normalization "makes queries faster" — its job is consistency; reads often get slower (more joins).
- Hunting for 2NF violations in single-column-key tables — partial dependencies need a composite key.
- Confusing 2NF and 3NF: partial = depends on part of the key; transitive = depends on another non-key column.
- Storing lists in one cell "because it's just one small field" — the 1NF debt always comes due as LIKE-search pain.
- Denormalizing casually, without a measured read-performance reason.
Quick recap
| Form | Forbids | Smell to look for |
|---|---|---|
| 1NF | non-atomic cells, repeating groups | commas inside a cell; phone1/phone2/phone3 |
| 2NF | partial dependency on a composite key | product details inside order_items |
| 3NF | transitive dependency via a non-key column | dept_name riding inside students |
| BCNF | any determinant that isn't a candidate key | teacher → subject in a (student, subject) table |
| Denormalization | (deliberately un-forbids) | read-heavy hotspots; pays with update anomalies |
Practice Zone — PYQs from real selection rounds
Schema-surgery time: you'll fix a 1NF violation, decompose a 2NF offender, and hunt down the inconsistency a denormalized table allowed in.
What is the primary goal of database normalization?
Asked in


A table has columns phone1, phone2, phone3 — and some users have four numbers. Which normal form does this design violate?
Asked in


2NF violations (partial dependencies) can only occur in tables that have…
Asked in


students(student_id PK, name, dept_id, dept_name) — dept_name depends on dept_id, which depends on student_id. What's this called, and which form does it break?
Asked in


A read-heavy analytics dashboard deliberately stores customer_name on every order row to avoid joins. What did the designer trade away?
Asked in


What does BCNF require that plain 3NF doesn't?
Asked in


The contacts_unnormalized table stores each person's phone numbers as a single comma-separated string, violating 1NF. Redesign it into proper 1NF by writing the CREATE TABLE statements for the corrected structure.
Asked in


contacts_unnormalized2 rows
| 1 | Asha | 9876543210,9123456789 |
| 2 | Ravi | 9988776655 |
order_items_flat has composite primary key (order_id, product_id), but product_name and product_price depend only on product_id — a partial dependency violating 2NF. Write CREATE TABLE statements that properly decompose this into 2NF.
Asked in


order_items_flat3 rows
| order_id | product_id | product_name | product_price | quantity |
|---|---|---|---|---|
| 101 | 1 | Notebook | 50.00 | 3 |
| 101 | 2 | Pen | 10.00 | 5 |
| 102 | 1 | Notebook | 50.00 | 2 |
customer_orders is denormalized — customer_name and customer_city repeat on every order row, and the same customer's city has been entered inconsistently over time. Write a query to find every customer_id that has more than one distinct city recorded.
Asked in


customer_orders6 rows
| order_id | customer_id | customer_name | customer_city | order_total |
|---|---|---|---|---|
| 1 | 501 | Asha Rao | Bengaluru | 1200.00 |
| 2 | 501 | Asha Rao | Bengaluru | 800.00 |
| 3 | 502 | Ravi Kumar | Pune | 500.00 |
| 4 | 501 | Asha Rao | Banglore | 300.00 |
| 5 | 503 | Meera Nair | Chennai | 950.00 |
| 6 | 502 | Ravi Kumar | Pune | 700.00 |
FAQ
Do real companies normalize to BCNF?
Mostly to 3NF — it removes virtually all practical anomalies. BCNF (and 4NF/5NF beyond it) matters in specific academic or data-modelling corners. In interviews, know BCNF's definition and one 3NF-but-not-BCNF example; in projects, 3NF with conscious denormalization is the industry default.
Is normalization related to constraints and keys?
Deeply — normalization decides which tables exist and what lives where, and the keys and foreign keys then enforce the relationships the decomposition created. Normalization is the architecture; constraints are the enforcement.
What are insert/update/delete anomalies in one line each?
Update: one fact stored many times needs many updates. Insert: a fact can't be recorded without an unrelated fact existing. Delete: removing one fact accidentally destroys another. All three come from redundancy; normalization removes the redundancy.
Next lesson: why the same query can take 2 seconds or 2 milliseconds — Lesson 9: Indexing & Query Optimization →


