StackPractices
intermediate By Mathias Paulenko

Database Normalization — 1NF to 5NF Explained

A visual guide to database normalization: learn 1NF through 5NF with practical examples, when to apply each form, and how to balance normalization with performance.

Overview

Normalization takes a raw table — one where the same fact lives in twenty rows and updating one place breaks another — and splits it into smaller tables that each describe one thing. The normal forms (1NF through 5NF) are a ladder of rules that remove a specific kind of redundancy at each rung. Most production schemas stop at 3NF or BCNF; the higher forms matter for interviews, weird edge cases, and knowing exactly what you’re giving up when you denormalize on purpose.

When to Use

  • Designing new relational schemas from scratch
  • Refactoring legacy databases with duplicate data
  • Preparing schemas for transactional workloads (OLTP)
  • Before deciding what to denormalize for reporting (OLAP) — see the companion Database Denormalization Guide for the flip side

1NF — Atomic Values

Rule: Every column contains only atomic (indivisible) values. No repeating groups.

Before — a repeating group in one column:

order_idcustomerproducts
1AliceApple, Banana, Cherry

After — atomic values only:

order_idcustomerproduct
1AliceApple
1AliceBanana
1AliceCherry

2NF — No Partial Dependencies

Rule: All non-key attributes depend on the entire primary key (relevant for composite keys).

Before — depends on part of the key:

course_idstudent_idcourse_namestudent_namegrade
CS101S1Intro to CSAliceA

course_name depends only on course_id; student_name only on student_id.

After — everything depends on the whole key:

Enrollments:

course_idstudent_idgrade
CS101S1A

Courses:

course_idcourse_name
CS101Intro to CS

Students:

student_idstudent_name
S1Alice

3NF — No Transitive Dependencies

Rule: Non-key attributes depend only on the primary key, not on other non-key attributes.

Before — a non-key column determines another:

employee_idnamedepartment_iddepartment_namedepartment_head
E1BobD1EngineeringCarol

department_name and department_head depend on department_id, not employee_id.

After — facts about the key, nothing else:

Employees:

employee_idnamedepartment_id
E1BobD1

Departments:

department_iddepartment_namedepartment_head
D1EngineeringCarol

BCNF — Boyce-Codd Normal Form

Rule: For every functional dependency X → Y, X must be a superkey.

Before — a non-key determinant:

studentcourseprofessor
AliceCS101Prof. Smith
BobCS101Prof. Smith

course → professor, but course is not a superkey.

After — every determinant is a key:

Enrollments:

studentcourse
AliceCS101
BobCS101

CourseAssignments:

courseprofessor
CS101Prof. Smith

4NF — No Multi-Valued Dependencies

Rule: No multi-valued dependencies except those on a superkey.

Before — two independent facts per row:

employeeskilllanguage
AliceJavaEnglish
AliceJavaSpanish
AlicePythonEnglish
AlicePythonSpanish

Skills and languages are independent multi-valued facts.

After — independent facts live apart:

EmployeeSkills:

employeeskill
AliceJava
AlicePython

EmployeeLanguages:

employeelanguage
AliceEnglish
AliceSpanish

5NF — Join Dependency / Projected Join

Rule: Every join dependency is implied by the candidate keys.

Before — a joinable triplet forced into pairs:

agentcompanyproduct
SmithFordTruck
SmithFordCar
SmithToyotaCar
JonesToyotaCar

After — decomposed along the join dependency:

AgentCompany:

agentcompany
SmithFord
SmithToyota
JonesToyota

AgentProduct:

agentproduct
SmithTruck
SmithCar
JonesCar

CompanyProduct:

companyproduct
FordTruck
FordCar
ToyotaCar

Normalization Summary

FormRuleEliminates
1NFAtomic valuesRepeating groups
2NFFull key dependencyPartial dependencies
3NFKey-only dependencyTransitive dependencies
BCNFSuperkey determinantRemaining anomalies
4NFNo multi-valued depsIndependent multi-values
5NFJoin dependenciesReconstructable joins
Mermaid flowchart LR diagram

Each step removes one class of redundancy — stop where your workload stops needing it.

When to Stop Normalizing

  • 3NF/BCNF is the practical stopping point for most OLTP systems
  • 4NF matters when you have true multi-valued attributes (rare)
  • 5NF is mostly theoretical for production applications
  • Denormalize intentionally when read performance matters more than write integrity — the denormalization guide covers the patterns and sync strategies

Common Mistakes

  • Over-normalizing to 5NF — adds complexity with minimal practical benefit
  • Under-normalizing to 1NF — leads to update anomalies and data inconsistency
  • Normalizing before understanding queries — the schema should serve the workload
  • Ignoring BCNF — 3NF does not handle all anomalies; BCNF is the stricter standard

Example: Normalization Steps

-- 1NF: Remove repeating groups
-- Unnormalized: orders(id, customer_name, items_csv)
-- 1NF:         orders(id, customer_name, item_name, qty)

-- 2NF: Remove partial dependencies (composite key)
-- 1NF:  order_items(order_id, product_id, product_name, qty)
-- 2NF:  orders(order_id, customer_id)
--       products(product_id, product_name)
--       order_items(order_id, product_id, qty)

-- 3NF: Remove transitive dependencies
-- 2NF:  orders(order_id, customer_id, customer_name, customer_city)
-- 3NF:  orders(order_id, customer_id)
--       customers(customer_id, customer_name, customer_city)

CREATE TABLE customers (
  customer_id SERIAL PRIMARY KEY,
  customer_name VARCHAR(200) NOT NULL,
  customer_city VARCHAR(100)
);

CREATE TABLE orders (
  order_id SERIAL PRIMARY KEY,
  customer_id INT REFERENCES customers(customer_id),
  order_date DATE NOT NULL DEFAULT CURRENT_DATE
);

CREATE TABLE order_items (
  order_id INT REFERENCES orders(order_id),
  product_id INT REFERENCES products(product_id),
  qty INT NOT NULL CHECK (qty > 0),
  PRIMARY KEY (order_id, product_id)
);