Functional Dependencies and Normal Forms: 1NF, 2NF, 3NF, BCNF
What This Concept Is
A functional dependency (FD) X -> Y on a relation R means: for any two tuples of R that agree on X, they also agree on Y. X determines Y.
Normal forms are a ladder of schema-shape rules, each eliminating a class of update anomalies (insertion, deletion, update anomalies) that result from storing the same fact in more than one place.
- 1NF: every attribute value is atomic. No arrays, no "comma-separated lists," no repeating groups. This is the minimum to call something a relation.
- 2NF: 1NF plus no non-key attribute depends on only part of a composite key. If the key is a single attribute, 2NF is automatic.
- 3NF: 2NF plus no non-key attribute depends on another non-key attribute (no transitive dependencies through a non-key).
- BCNF (Boyce-Codd): for every non-trivial FD
X -> Y,Xis a superkey. BCNF is strictly stronger than 3NF in rare cases; most schemas that are 3NF are also BCNF.
The discipline is: identify the FDs that hold in your domain, then decompose offending relations into smaller ones until the normal form you want holds. A good decomposition is lossless (joining the parts reproduces the original) and ideally dependency-preserving.
Why It Matters Here
- every "update this field in 17 rows" problem is a normal-form violation
- denormalization (Concept 8) only makes sense if you can recognize what you are denormalizing away from
- foreign keys are meaningful only when the referenced relation satisfies at least 3NF on the referenced attribute
- in interviews and design reviews, "is this at least in 3NF?" is a standard gate
Concrete Example
Unnormalized table (violates 3NF):
StudentCourse(student_id, student_name, student_email, course_code, course_title, instructor, grade)
FDs in the domain:
student_id -> student_name, student_emailcourse_code -> course_title, instructor(student_id, course_code) -> grade
The first two FDs have non-key determinants (only the composite (student_id, course_code) is a key). Those are transitive dependencies, so the table is 2NF at best. It carries three different facts:
- "student 42 is Alice Li": repeated in every one of Alice's rows
- "course CS101 is taught by Prof X": repeated in every enrolled row
- "Alice got a B in CS101": the actual enrollment fact
3NF decomposition:
CREATE TABLE student(
id INT PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);
CREATE TABLE course(
code TEXT PRIMARY KEY,
title TEXT NOT NULL,
instructor TEXT NOT NULL
);
CREATE TABLE enrollment(
student_id INT REFERENCES student(id),
course_code TEXT REFERENCES course(code),
grade TEXT,
PRIMARY KEY (student_id, course_code)
);
Now each fact lives in exactly one place. Changing Alice's email is one UPDATE, not N.