4.4.21 · D2Databases

Visual walkthrough — Normalization — 1NF, 2NF, 3NF, BCNF — anomalies each resolves

2,075 words9 min readBack to topic

Before any symbol appears, three words we will lean on the whole way:

We also need the arrow. When we write (read " arrow ") we mean: knowing pins down . If two rows show the same , they are forced to show the same . That arrow is our only piece of notation; everything else is grids and colours. (Full treatment: Functional Dependencies.)


Step 1 — The table we start with

WHAT. A school stuffs everything about enrollments into ONE table:

Term by term, what each column is:

  • — the code for a student (S1, S2, …). Underlined = part of the key.
  • — the code for a course (C1, C2, …). Also underlined = part of the key.
  • — the student's human name.
  • — the mark that student got in that course.
  • — the teacher who advises that student.
  • — the department that advisor belongs to.

WHY start here. This table looks tidy but is secretly three facts glued together: "who a student is", "what grade they got", "which department advises them". We will pry them apart.

PICTURE. Look at the grid. The pink columns repeat the same value every time a student appears — that repetition is the enemy.

Figure — Normalization — 1NF, 2NF, 3NF, BCNF — anomalies each resolves

Step 2 — 1NF: make every cell atomic

WHAT. Suppose someone had stored the phones as one cell: . Atomic means one value per cell — no lists hiding inside.

WHY this tool, why now. Before we can even talk about which column determines which, each cell must hold exactly one thing. A list in a cell is invisible to the database: you cannot ask "who owns 9992?" because 9992 is buried in text. So the very first repair is: explode every list into its own rows.

PICTURE. On the left, one row with a two-item cell. On the right, two rows, each with a single phone. Same facts — now reachable.

Figure — Normalization — 1NF, 2NF, 3NF, BCNF — anomalies each resolves

Step 3 — Spot the partial dependency (the 2NF offender)

WHAT. Ask of each non-key column: does it really need the whole key, or just part of it?

  • needs both and (a grade is per student per course). Good — full key.
  • needs only . The course is irrelevant to your name.

So we have found a partial dependency:

  • — a proper subset of the key (proper = strictly smaller, leaves out ).
  • The arrow — "determines": same forces same .
  • — a non-prime column (belongs to no key), so it is the kind of column the rule protects.

WHY this is a bug. Ana takes 4 courses → her name is written 4 times. Rename her and you must fix all 4 rows; miss one → the table disagrees with itself (update anomaly). Drop her last enrollment → her name vanishes entirely (delete anomaly).

PICTURE. The arrow reaches from only half the key. Watch the blue cell repeat down the rows — that column is copied, wastefully.

Figure — Normalization — 1NF, 2NF, 3NF, BCNF — anomalies each resolves

Step 4 — 2NF: split the partial dependency out

WHAT. Carve into its own table, keyed by the part it actually depends on:

WHY it works. Now each student's name is stored once in . keeps only what genuinely needs the whole pair-key () plus the advisor columns we still owe attention to. The join back is lossless because is the shared link (see Decomposition — Lossless Join and Dependency Preservation).

PICTURE. The blue name column lifts out of the big grid into a small 2-column table. Where four "Ana" cells stood, one remains.

Figure — Normalization — 1NF, 2NF, 3NF, BCNF — anomalies each resolves

Step 5 — Spot the transitive dependency (the 3NF offender)

WHAT. Look inside the new at the advisor columns. Two real rules:

Chain them:

This chain means transitively — the key reaches only through the non-key middleman . (For simplicity here read as the determinant; the point is the middleman is not a key.)

  • The middle arrow's left side, , is not a superkey — it does not identify a row of Enroll.
  • is non-prime — belongs to no candidate key.
  • Both conditions true at once ⇒ 3NF is violated.

WHY this is a bug. Every student advised by teacher T repeats T's department. Move the advisor's department once in the real world → hunt down every student row (update anomaly). The last student of an advisor leaves → the advisor's department is lost (delete anomaly).

PICTURE. A two-hop arrow: key → middleman → tag-along. The yellow cell repeats for every student of the same advisor.

Figure — Normalization — 1NF, 2NF, 3NF, BCNF — anomalies each resolves

Step 6 — 3NF: cut the chain at the middleman

WHAT. Give the middleman its own table:

WHY it works. Now lives once per advisor. keeps only as a pointer. The chain is broken: nothing non-key determines another non-key anymore.

PICTURE. The yellow department column detaches into a tiny table; Enroll keeps just the arrow-pointer .

Figure — Normalization — 1NF, 2NF, 3NF, BCNF — anomalies each resolves

Step 7 — The BCNF loophole: a table that passes 3NF but still leaks

WHAT. A different corner of the school: who teaches whom.

with two real rules:

  • each — a student studies a subject with one teacher.
  • each — a teacher teaches exactly one subject.

Work out the keys: from , knowing gives , so is a key. And is a key too. Therefore both and are prime (each sits in some key).

Now test the FD :

  • is not a superkey (a teacher alone does not identify a row).
  • but is prime → 3NF's escape hatch says "fine!". 3NF passes.
  • BCNF has no escape hatch → BCNF fails.

WHY this still bugs. You cannot record "Teacher T teaches Subject Y" until some student enrolls — the row needs a (insert anomaly). The fact "T teaches Y" is homeless.

PICTURE. The FD arrow starts at a non-superkey; the pink cell shows duplicated for every student of that teacher, yet 3NF waves it through because is prime.

Figure — Normalization — 1NF, 2NF, 3NF, BCNF — anomalies each resolves

Step 8 — BCNF: every determinant must be a superkey

WHAT. Split on the offending determinant :

WHY it works. Now "T teaches Y" lives in with as its key — a determinant that is a superkey. You can register a teacher's subject with no student present. Every remaining FD has a superkey on its left.

PICTURE. becomes its own clean 2-column table; the leftover links students to teachers.

Figure — Normalization — 1NF, 2NF, 3NF, BCNF — anomalies each resolves

The one-picture summary

Every squeeze is the same move: find an arrow whose left side is too small or non-key, and lift the arrow into its own table. 1NF fixes cells; 2NF fixes "part of key"; 3NF fixes "non-key middleman"; BCNF fixes "any non-superkey determinant".

Figure — Normalization — 1NF, 2NF, 3NF, BCNF — anomalies each resolves
Recall Feynman retelling of the whole walkthrough

We started with one giant notebook page where a student's name, grade, and advisor's department were all crammed on each enrollment line. 1NF: stop stuffing lists in a box — one thing per box, so you can actually search it. 2NF: the student's name only cares about the student, not the course, yet we rewrote it for every course — so we moved names to their own little "Student" book, written once. 3NF: the advisor's department only cares about the advisor, and the advisor is not the key — a middleman — so we gave advisors their own "Advisor" book. BCNF: in a separate teaching table, a rule "each teacher teaches one subject" duplicated the subject over and over, and 3NF let it slide because "subject" happened to be part of a key — but you still couldn't record a teacher's subject before a student showed up. BCNF says no left-side may be a non-key, so we gave teachers-and-their-subjects their own book too. Each step: spot an arrow starting from something too small, and give that arrow its own page — so every fact is written exactly once.

Recall Quick checks

Why is a partial dependency? ::: is a proper subset of the composite key and is non-prime. Why does violate 3NF? ::: is a non-key middleman and is non-prime — a transitive dependency. Why does pass 3NF but fail BCNF? ::: is not a superkey (fails BCNF) but is prime, so 3NF's "or is prime" hatch excuses it. What is the single repeated move across all forms? ::: Find an arrow whose left side is too small / non-key, and lift it into its own table.