Skip to main content

Normalization

Think of these are audits or sanity checks that make sure you’ve designed your database properly. Doing things properly means that the query planners and optimizers work well, that you enjoy all the benefits of consistency (the “C” in ACID.)

There are five levels.

The Progression​

Each form is the previous one plus one more rule. Every fix is the same move: split a table so that a fact is stored in exactly one place.

Why bother? Redundant facts cause anomalies:

  • Update: change a fact in one row, forget the other copies, now the DB disagrees with itself.
  • Insert: can’t record a fact without some unrelated fact (e.g. can’t add a patient until they have a diagnosis).
  • Delete: deleting one fact accidentally deletes another.

Vocab​

  • Key: column(s) that uniquely identify a row. Key columns are underlined in the tables below.
  • Functional dependency A→BA \to B: knowing AA tells you BB. Each value of AA goes with exactly one value of BB. E.g. pid→namepid \to name.
  • Multivalued dependency A↠BA \twoheadrightarrow B: knowing AA tells you a whole set of BBs, independent of the other columns. E.g. a patient has a set of allergies.

Redundant values are shown in red\color{red}\text{red}.


Unnormalized​

A spreadsheet someone emailed you:

nameagediagnosesAna34flu, asthmaCy"seventy-one"copdAna34flu\begin{array}{|c|c|c|} \hline \textbf{name} & \textbf{age} & \textbf{diagnoses} \\ \hline \text{Ana} & 34 & {\color{red}\text{flu, asthma}} \\ \hline \text{Cy} & {\color{red}\text{"seventy-one"}} & \text{copd} \\ \hline \text{Ana} & 34 & \text{flu} \\ \hline \end{array}

Problems: a list crammed into one cell, a string in a number column, and no way to tell whether the two Anas are the same person. Plus, “the last row is the most recent visit” is information hiding in row order.

First Normal Form (1NF)​

  1. Using row order to convey information is not permitted
  2. Mixing data types within the same column is not permitted
  3. Having a table without a primary key is not permitted
  4. Repeating groups are not permitted

In plain terms: it’s an actual relation. One value per cell, one type per column, a key, and row order means nothing. This is just the definition of a relation from Relational Algebra: a set of tuples over fixed domains.

Fix: one row per (patient, diagnosis), store dates explicitly, add IDs. Key is (pid,icd)(pid, icd). We’ve also pulled in the patient’s primary care doctor (PCP):

pid‾icd‾namepcpphonedate1J11AnaDr. Lee555-0101Jan 51J45AnaDr. Lee555-0101Feb 103J44CyDr. Kim555-0199Mar 153J11CyDr. Kim555-0199Apr 20\begin{array}{|c|c|c|c|c|c|} \hline \underline{\textbf{pid}} & \underline{\textbf{icd}} & \textbf{name} & \textbf{pcp} & \textbf{phone} & \textbf{date} \\ \hline 1 & \text{J11} & \text{Ana} & \text{Dr. Lee} & \text{555-0101} & \text{Jan 5} \\ \hline 1 & \text{J45} & {\color{red}\text{Ana}} & {\color{red}\text{Dr. Lee}} & {\color{red}\text{555-0101}} & \text{Feb 10} \\ \hline 3 & \text{J44} & \text{Cy} & \text{Dr. Kim} & \text{555-0199} & \text{Mar 15} \\ \hline 3 & \text{J11} & {\color{red}\text{Cy}} & {\color{red}\text{Dr. Kim}} & {\color{red}\text{555-0199}} & \text{Apr 20} \\ \hline \end{array}

Valid 1NF, but Ana’s name and doctor are stored once per diagnosis.

Second Normal Form (2NF)​

Each non-key attribute in the table must be dependent on the entire primary key.

In plain terms: no column depends on just part of the key. Only matters when the key has multiple columns.

Problem: pid→name,pcp,phonepid \to name, pcp, phone. None of those care about icdicd. Only datedate needs the whole key (pid,icd)(pid, icd).

Fix: split the patient facts out.

Patientpid‾namepcpphone1AnaDr. Lee555-01012BoDr. Lee555-01013CyDr. Kim555-0199Diagnosispid‾icd‾date1J11Jan 51J45Feb 103J44Mar 153J11Apr 20\begin{array}{c} \textbf{Patient} \\[4pt] \begin{array}{|c|c|c|c|} \hline \underline{\textbf{pid}} & \textbf{name} & \textbf{pcp} & \textbf{phone} \\ \hline 1 & \text{Ana} & \text{Dr. Lee} & \text{555-0101} \\ \hline 2 & \text{Bo} & \text{Dr. Lee} & {\color{red}\text{555-0101}} \\ \hline 3 & \text{Cy} & \text{Dr. Kim} & \text{555-0199} \\ \hline \end{array} \end{array} \qquad \begin{array}{c} \textbf{Diagnosis} \\[4pt] \begin{array}{|c|c|c|} \hline \underline{\textbf{pid}} & \underline{\textbf{icd}} & \textbf{date} \\ \hline 1 & \text{J11} & \text{Jan 5} \\ \hline 1 & \text{J45} & \text{Feb 10} \\ \hline 3 & \text{J44} & \text{Mar 15} \\ \hline 3 & \text{J11} & \text{Apr 20} \\ \hline \end{array} \end{array}

Ana’s name is now stored once. Bonus: we can now add Bo, who has no diagnoses yet (insert anomaly gone).

Third Normal Form (3NF)​

Each non-key attribute in the table must depend on the key, the whole key, and nothing but the key.

In plain terms: no column depends on another non-key column. No chains like key→X→Ykey \to X \to Y.

Problem: pid→pcp→phonepid \to pcp \to phone. The phone belongs to the doctor, not the patient. Dr. Lee’s number is stored once per patient. If it changes, every one of Lee’s patients’ rows needs updating.

Fix: give doctors their own table.

Patientpid‾namepcp1AnaDr. Lee2BoDr. Lee3CyDr. KimDoctorpcp‾phoneDr. Lee555-0101Dr. Kim555-0199\begin{array}{c} \textbf{Patient} \\[4pt] \begin{array}{|c|c|c|} \hline \underline{\textbf{pid}} & \textbf{name} & \textbf{pcp} \\ \hline 1 & \text{Ana} & \text{Dr. Lee} \\ \hline 2 & \text{Bo} & \text{Dr. Lee} \\ \hline 3 & \text{Cy} & \text{Dr. Kim} \\ \hline \end{array} \end{array} \qquad \begin{array}{c} \textbf{Doctor} \\[4pt] \begin{array}{|c|c|} \hline \underline{\textbf{pcp}} & \textbf{phone} \\ \hline \text{Dr. Lee} & \text{555-0101} \\ \hline \text{Dr. Kim} & \text{555-0199} \\ \hline \end{array} \end{array}

(Diagnosis unchanged.) This is where most real schemas stop. Mnemonic: “the key, the whole key, and nothing but the key, so help me Codd.” 1NF = there is a key, 2NF = the whole key, 3NF = nothing but the key.

Boyce-Codd Normal Form (BCNF)​

Each attribute in the table must depend on the key, the whole key, and nothing but the key.

In plain terms: if any column determines another, it must be a key. 3NF only polices non-key columns. BCNF polices all of them. The difference only shows up when a non-key column determines part of the key.

New table. Rules: a patient sees one doctor per specialty, and each specialist practices one specialty. Key is (pid,specialty)(pid, specialty):

pid‾specialty‾doctor1cardioDr. Shah1dermDr. Ito3cardioDr. Shah3dermDr. Park\begin{array}{|c|c|c|} \hline \underline{\textbf{pid}} & \underline{\textbf{specialty}} & \textbf{doctor} \\ \hline 1 & \text{cardio} & \text{Dr. Shah} \\ \hline 1 & \text{derm} & \text{Dr. Ito} \\ \hline 3 & {\color{red}\text{cardio}} & \text{Dr. Shah} \\ \hline 3 & \text{derm} & \text{Dr. Park} \\ \hline \end{array}

Problem: doctor→specialtydoctor \to specialty, but doctordoctor isn’t a key. Passes 3NF (because specialtyspecialty is part of the key), but “Shah is a cardiologist” is stored twice. Delete Cy’s derm row and you lose the fact that Park is a dermatologist.

Fix:

Specialistdoctor‾specialtyDr. ShahcardioDr. ItodermDr. ParkdermReferralpid‾doctor‾1Dr. Shah1Dr. Ito3Dr. Shah3Dr. Park\begin{array}{c} \textbf{Specialist} \\[4pt] \begin{array}{|c|c|} \hline \underline{\textbf{doctor}} & \textbf{specialty} \\ \hline \text{Dr. Shah} & \text{cardio} \\ \hline \text{Dr. Ito} & \text{derm} \\ \hline \text{Dr. Park} & \text{derm} \\ \hline \end{array} \end{array} \qquad \begin{array}{c} \textbf{Referral} \\[4pt] \begin{array}{|c|c|} \hline \underline{\textbf{pid}} & \underline{\textbf{doctor}} \\ \hline 1 & \text{Dr. Shah} \\ \hline 1 & \text{Dr. Ito} \\ \hline 3 & \text{Dr. Shah} \\ \hline 3 & \text{Dr. Park} \\ \hline \end{array} \end{array}

Trade-off: “one doctor per specialty per patient” is no longer enforceable by a simple key constraint, because the two columns now live in different tables. This is why people sometimes stay at 3NF.

Fourth Normal Form (4NF)​

The only kinds of multivalued dependency allowed in a table are multivalued dependencies on the key.

In plain terms: don’t store two independent lists in one table.

Ana has allergies penicillin,peanuts{penicillin, peanuts} and meds albuterol,oseltamivir{albuterol, oseltamivir}. These have nothing to do with each other, but in one table every allergy must be paired with every med. Key is all three columns:

pid‾allergy‾med‾1penicillinalbuterol1penicillinoseltamivir1peanutsalbuterol1peanutsoseltamivir\begin{array}{|c|c|c|} \hline \underline{\textbf{pid}} & \underline{\textbf{allergy}} & \underline{\textbf{med}} \\ \hline 1 & \text{penicillin} & \text{albuterol} \\ \hline 1 & \text{penicillin} & \text{oseltamivir} \\ \hline 1 & {\color{red}\text{peanuts}} & {\color{red}\text{albuterol}} \\ \hline 1 & {\color{red}\text{peanuts}} & {\color{red}\text{oseltamivir}} \\ \hline \end{array}

No functional dependencies at all, so it’s BCNF. But pid↠allergypid \twoheadrightarrow allergy and pid↠medpid \twoheadrightarrow med. The table is literally a Cartesian product: {penicillin,peanuts}×{albuterol,oseltamivir}\{\text{penicillin}, \text{peanuts}\} \times \{\text{albuterol}, \text{oseltamivir}\}. Adding a third med means adding two rows, one per allergy. Forget one and you’ve implied Ana isn’t allergic to something.

Fix: one table per list.

Allergypid‾allergy‾1penicillin1peanutsMedicationpid‾med‾1albuterol1oseltamivir\begin{array}{c} \textbf{Allergy} \\[4pt] \begin{array}{|c|c|} \hline \underline{\textbf{pid}} & \underline{\textbf{allergy}} \\ \hline 1 & \text{penicillin} \\ \hline 1 & \text{peanuts} \\ \hline \end{array} \end{array} \qquad \begin{array}{c} \textbf{Medication} \\[4pt] \begin{array}{|c|c|} \hline \underline{\textbf{pid}} & \underline{\textbf{med}} \\ \hline 1 & \text{albuterol} \\ \hline 1 & \text{oseltamivir} \\ \hline \end{array} \end{array}

4 rows became 2 + 2. With kk allergies and jj meds it’s k×jk \times j vs k+jk + j.

Fifth Normal Form (5NF)​

It must not be possible to describe the table as being the logical result of joining some other tables together.

In plain terms: if a table can be rebuilt by joining smaller tables, store the smaller tables instead.

Hospital rule: a doctor performs a procedure at a hospital whenever the doctor knows the procedure, the doctor has privileges at the hospital, and the hospital offers the procedure.

doctor‾procedure‾hospital‾Dr. ShahangioMercyDr. ShahangioSt. Luke’sDr. ShahstentMercyDr. ItostentMercy\begin{array}{|c|c|c|} \hline \underline{\textbf{doctor}} & \underline{\textbf{procedure}} & \underline{\textbf{hospital}} \\ \hline \text{Dr. Shah} & \text{angio} & \text{Mercy} \\ \hline \text{Dr. Shah} & \text{angio} & \text{St. Luke's} \\ \hline \text{Dr. Shah} & \text{stent} & \text{Mercy} \\ \hline \text{Dr. Ito} & \text{stent} & \text{Mercy} \\ \hline \end{array}

No independent lists (Shah does stents, has St. Luke’s privileges, but doesn’t do stents at St. Luke’s, since they don’t offer them), so it’s 4NF. But because of the rule, this table is exactly the join of three pairwise facts:

Skilldoctor‾procedure‾Dr. ShahangioDr. ShahstentDr. ItostentPrivilegedoctor‾hospital‾Dr. ShahMercyDr. ShahSt. Luke’sDr. ItoMercyOffershospital‾procedure‾MercyangioMercystentSt. Luke’sangio\begin{array}{c} \textbf{Skill} \\[4pt] \begin{array}{|c|c|} \hline \underline{\textbf{doctor}} & \underline{\textbf{procedure}} \\ \hline \text{Dr. Shah} & \text{angio} \\ \hline \text{Dr. Shah} & \text{stent} \\ \hline \text{Dr. Ito} & \text{stent} \\ \hline \end{array} \end{array} \qquad \begin{array}{c} \textbf{Privilege} \\[4pt] \begin{array}{|c|c|} \hline \underline{\textbf{doctor}} & \underline{\textbf{hospital}} \\ \hline \text{Dr. Shah} & \text{Mercy} \\ \hline \text{Dr. Shah} & \text{St. Luke's} \\ \hline \text{Dr. Ito} & \text{Mercy} \\ \hline \end{array} \end{array} \qquad \begin{array}{c} \textbf{Offers} \\[4pt] \begin{array}{|c|c|} \hline \underline{\textbf{hospital}} & \underline{\textbf{procedure}} \\ \hline \text{Mercy} & \text{angio} \\ \hline \text{Mercy} & \text{stent} \\ \hline \text{St. Luke's} & \text{angio} \\ \hline \end{array} \end{array} Original=Skill⋈Privilege⋈Offers\text{Original} = \text{Skill} \bowtie \text{Privilege} \bowtie \text{Offers}

Problem with the 3-column table: if St. Luke’s starts offering stents, you have to remember to add a row for every doctor who knows stents and has privileges there. In 5NF you add one row to Offers and the join produces the rest.

Subtle bit: you need all three. Joining just Skill⋈Privilege\text{Skill} \bowtie \text{Privilege} invents a fake row (Shah, stent, St. Luke’s). And if the business rule doesn’t hold (doctors are credentialed per procedure per hospital), the 3-column table is irreducible and already 5NF. 5NF depends on what’s true in the world, not just on what the data looks like.


Summary​

FormPlain ruleTypical fix
1NFIt’s a real relation: atomic cells, a key, no row orderOne row per fact, add IDs
2NFNo column depends on part of the keySplit off the partial dependency
3NFNo column depends on a non-key columnSplit off the chain (X→YX \to Y)
BCNFEvery determinant is a keySplit off the determinant
4NFNo independent lists in one tableOne table per list
5NFNo table that’s just a join of smaller onesStore the smaller ones

In practice: aim for 3NF/BCNF in transactional (OLTP) databases. 4NF/5NF problems are rare and usually get fixed by common sense. Analytical databases (data warehouses, star schemas) often denormalize on purpose, trading redundancy for fewer joins.