Skip to content

Database Normalization

Database normalization is the systematic process of structuring a relational schema to minimize data Redundancy and eliminate insertion, deletion, and update anomalies. The theory was introduced by E.F. Codd in 1970 and formalized through a series of normal forms, each defined in terms of Functional dependencies on the relation.

The core idea is simple: every piece of data should live in exactly one place. If the same fact Appears in multiple rows, updating that fact requires updating every row that contains it. If you Miss one, your data is inconsistent. Normalization gives you a principled, mathematical framework For avoiding this class of problems.

Unnormalized schemas suffer from three categories of anomalies:

Anomaly TypeDescriptionConcrete Example
InsertionCannot add a fact without adding unrelated factsCannot record a new department until at least one employee is assigned to it
DeletionDeleting one fact unintentionally removes anotherDeleting the last employee in a department also removes the department”s address
UpdateUpdating a single fact requires touching multiple rowsRenaming a department requires updating every employee row in that department

These are not theoretical concerns. In production systems with millions of rows, an update anomaly Means a single UPDATE statement that touches 50,000 rows, requires a table lock, and risks partial Failure. Normalization eliminates these problems at the schema level rather than relying on Application logic to keep data consistent.

Normalization and denormalization are not opposites in the sense that one is “right” and the other Is “wrong.” They are engineering tradeoffs:

Normalized schema:
+ Each fact stored once (single source of truth)
+ No update anomalies
+ Smaller individual tables
+ Schema is self-documenting through foreign keys
- Queries require JOINs (higher read latency)
- More complex queries for simple reports
Denormalized schema:
+ Fewer JOINs (lower read latency for common access patterns)
+ Simpler queries for reporting and dashboards
- Data duplication (write amplification)
- Update anomalies require application-level consistency logic
- Higher storage cost

The default starting point for any OLTP system is 3NF. Denormalize only after measuring a specific Performance bottleneck and understanding the consistency cost. Premature denormalization creates Maintenance debt that compounds over time.

Each normal form is a strict subset of the one below it:

1NF \supset 2NF \supset 3NF \supset \mathrm{BCNF \supset 4NF \supset 5NF

A relation in BCNF is automatically in 3NF, 2NF, and 1NF. The higher the normal form, the less Redundancy, but the more relations you need (and the more JOINs you must perform).

Functional dependencies are the mathematical foundation on which all normal forms are built. Before Discussing any normal form, you must understand FDs thoroughly.

Definition. Given a relation schema RR and two attribute sets XRX \subseteq R and YRY \subseteq RA functional dependency XYX \rightarrow Y holds on RR if and only if, for every Pair of tuples t1t_1 and t2t_2 in any legal instance of RR:

t1[X]=t2[X]    t1[Y]=t2[Y]t_1[X] = t_2[X] \implies t_1[Y] = t_2[Y]

In plain language: if two tuples agree on all attributes in XXThey must also agree on all Attributes in YY. XX is called the determinant and YY is called the dependent.

Definition. A functional dependency XYX \rightarrow Y is:

  • Trivial if YXY \subseteq X. It holds for every relation by definition and carries no informational content. Example: {A,B}{A}\{A, B\} \rightarrow \{A\}.
  • Non-trivial if Y⊈XY \not\subseteq X. It carries actual semantic information about the data.
  • Completely non-trivial if XY=X \cap Y = \emptyset.

Full vs Partial vs Transitive Dependencies

Section titled “Full vs Partial vs Transitive Dependencies”

These three categories are critical for understanding the progression from 2NF to 3NF:

Definition. Let KK be a candidate key of relation RR.

  • A full functional dependency KAK \rightarrow A means that AA depends on all of KK. Removing any attribute from KK destroys the dependency. Formally, for no proper subset KKK' \subset K does KAK' \rightarrow A hold.
  • A partial functional dependency KAK \rightarrow A means that AA depends on only a proper subset of KK. There exists some KKK' \subset K such that KAK' \rightarrow A holds.
  • A transitive dependency occurs when XYX \rightarrow Y and YZY \rightarrow Z both hold, and ZZ depends on XX only through the intermediate YY. Formally, XZX \rightarrow Z holds because XYX \rightarrow Y and YZY \rightarrow ZBut neither YXY \subseteq X nor ZXYZ \subseteq XY.
Relation: OrderItem(order_id, product_id, quantity, product_name, category_name)
Full dependency: {order_id, product_id} -> quantity
(quantity depends on the ENTIRE composite key)
Partial dependency: product_id -> product_name, category_name
(product_name depends on only product_id, a SUBSET of the key)
Transitive dependency: product_id -> category_id -> category_name
(category_name depends on product_id only through category_id)

Armstrong’s axioms are a sound and complete set of inference rules for deriving all functional Dependencies that are logically implied by a given set FF.

Definition. Given a set of functional dependencies FF on a relation schema RRThe three Axioms are:

  1. Reflexivity (A1): If YXY \subseteq XThen XYX \rightarrow Y.
  2. Augmentation (A2): If XYX \rightarrow YThen XZYZXZ \rightarrow YZ for any attribute set ZZ.
  3. Transitivity (A3): If XYX \rightarrow Y and YZY \rightarrow ZThen XZX \rightarrow Z.

These three axioms alone are sound (every derived dependency is correct) and complete (every correct Dependency can be derived from them).

The following rules are not axioms but can be proven from Armstrong’s three axioms. They are used Constantly in normalization proofs:

RuleStatementProof Strategy
UnionIf XYX \rightarrow Y and XZX \rightarrow ZThen XYZX \rightarrow YZAugmentation + Transitivity
DecompositionIf XYZX \rightarrow YZThen XYX \rightarrow Y and XZX \rightarrow ZReflexivity + Transitivity
PseudotransitivityIf XYX \rightarrow Y and YWZYW \rightarrow ZThen XWZXW \rightarrow ZAugmentation + Transitivity
CompositionIf XYX \rightarrow Y and WZW \rightarrow ZThen XWYZXW \rightarrow YZAugmentation + Union

Proof of the Union rule:

\begin{aligned} X \rightarrow Y \quad &\mathrm{(given) \\ X \rightarrow X \quad &\mathrm{(reflexivity, since X \subseteq X) \\ XX \rightarrow YX \quad &\mathrm{(augmentation, add X \mathrm{ to both sides) \\ X \rightarrow XY \quad &\mathrm{(since XX = X) \\ XY \rightarrow YZ \quad &\mathrm{(augmentation of X \rightarrow Z \mathrm{ with Y) \\ X \rightarrow YZ \quad &\mathrm{(transitivity) \end{aligned}

Definition. The closure of an attribute set XX under a set of functional dependencies FF Denoted X+X^+Is the largest attribute set such that XX+X \rightarrow X^+ can be derived from FF Using Armstrong’s axioms.

Algorithm to compute X+X^+:

function attributeClosure(X, F):
X⁺ = X
repeat until X⁺ stops changing:
for each FD Y -> Z in F:
if Y ⊆ X⁺:
X⁺ = X⁺ ∪ Z
return X⁺

Key property: XYX \rightarrow Y is logically implied by FF if and only if YX+Y \subseteq X^+.

This algorithm is used for two critical tasks: determining whether an attribute set is a superkey (X+=RX^+ = RI.e., the closure equals the entire relation schema), and determining whether a specific FD XYX \rightarrow Y is implied by FF (check whether YX+Y \subseteq X^+).

Relation: R(A, B, C, D, E)
FDs: {A -> B, BC -> D, D -> E}
Compute A⁺:
A⁺ = {A}
A -> B: A ⊆ A⁺, so A⁺ = {A, B}
BC -> D: {B,C} ⊈ A⁺, skip
D -> E: D ⊈ A⁺, skip
No more changes. A⁺ = {A, B}
A is NOT a superkey.
Compute AC⁺:
AC⁺ = {A, C}
A -> B: A ⊆ AC⁺, so AC⁺ = {A, B, C}
BC -> D: {B,C} ⊆ AC⁺, so AC⁺ = {A, B, C, D}
D -> E: D ⊆ AC⁺, so AC⁺ = {A, B, C, D, E}
AC⁺ = R. AC is a superkey.

Definition. The closure of a set of functional dependencies FFDenoted F+F^+Is the set of All FDs that can be derived from FF using Armstrong’s axioms.

F+F^+ can be exponentially large (up to 22n2^{2^n} FDs for nn attributes), so you never compute it Explicitly. Instead, you use the attribute closure algorithm to answer specific questions about Whether a given FD is in F+F^+.

To find all candidate keys of a relation RR given a set of FDs FF:

  1. Compute the closure of each attribute and each combination of attributes.
  2. An attribute set XX is a superkey if X+=RX^+ = R.
  3. A superkey XX is a candidate key if it is minimal: removing any attribute AA from XX yields (X{A})+R(X - \{A\})^+ \neq R.
Relation: R(A, B, C, D)
FDs: {A -> C, C -> B, D -> C}
Compute closures of single attributes:
A⁺ = {A, C, B} (A -> C -> B)
B⁺ = {B}
C⁺ = {C, B} (C -> B)
D⁺ = {D, C, B} (D -> C -> B)
None of A, B, C, D alone is a superkey (none has D in its closure except D⁺,
but D⁺ = {D,C,B} which lacks A).
Check pairs:
AD⁺ = {A, D, C, B} = R. AD is a superkey.
Is AD minimal?
A⁺ = {A, C, B} ≠ R (missing D). So A alone is not a superkey.
D⁺ = {D, C, B} ≠ R (missing A). So D alone is not a superkey.
AD is a candidate key.
BD⁺ = {B, D, C} ≠ R (missing A). Not a superkey.
AB⁺ = {A, B, C} ≠ R (missing D). Not a superkey.
CD⁺ = {C, D, B} ≠ R (missing A). Not a superkey.
AC⁺ = {A, C, B} ≠ R (missing D). Not a superkey.
The only candidate key is {A, D}.
Prime attributes: A, D.
Non-prime attributes: B, C.

Definition. A minimal cover FminF_{min} of a set of FDs FF satisfies three conditions:

  1. Right-side decomposition: Every FD in FminF_{min} has exactly one attribute on the right side. (Replace XYZX \rightarrow YZ with XYX \rightarrow Y and XZX \rightarrow Z.)
  2. No redundant FDs: Removing any FD from FminF_{min} changes the closure. For each fFminf \in F_{min}, (Fmin{f})+Fmin+(F_{min} - \{f\})^+ \neq F_{min}^+.
  3. No redundant attributes on the left side: For each FD XAX \rightarrow A in FminF_{min} and each attribute BXB \in X, (X{B})+(X - \{B\})^+ does not contain AA. In other words, removing any attribute from the left side would destroy the dependency.

The minimal cover is not necessarily unique, but all minimal covers of FF are equivalent.

Algorithm to compute FminF_{min}:

1. START with F.
2. DECOMPOSE right sides:
Replace each X -> {A₁, A₂, ..., Aₙ} with X -> A₁, X -> A₂, ..., X -> Aₙ.
3. REMOVE redundant FDs:
For each FD f in F:
Temporarily remove f from F.
Compute closure of f's left side using the remaining FDs.
If the closure still contains f's right side, f is redundant -- remove it permanently.
Otherwise, restore f.
4. REMOVE redundant attributes from left sides:
For each FD X -> A in F:
For each attribute B in X:
Temporarily remove B from X.
Compute closure of (X - {B}) using the full set of FDs.
If the closure contains A, B is redundant -- remove it permanently from X.
Otherwise, restore B.
5. RETURN the result.
Example: Compute minimal cover of F = {AB -> C, C -> B, A -> B}
Step 1: Right sides already single-attribute.
Step 2: Remove redundant FDs:
Remove AB -> C:
Compute (AB)⁺ using {C -> B, A -> B}:
AB⁺ = {A, B}. Does NOT contain C. Restore AB -> C.
Remove C -> B:
Compute C⁺ using {AB -> C, A -> B}:
C⁺ = {C}. Does NOT contain B. Restore C -> B.
Remove A -> B:
Compute A⁺ using {AB -> C, C -> B}:
A⁺ = {A}. Does NOT contain B. Restore A -> B.
No redundant FDs.
Step 3: Remove redundant attributes:
AB -> C:
Remove A: compute B⁺ using {B -> ?, C -> B, A -> B}:
B⁺ = {B}. Does NOT contain C. A is not redundant.
Remove B: compute A⁺ using {A -> ?, C -> B, A -> B}:
A⁺ = {A, B, C} (A -> B, AB -> C). Contains C. B IS redundant.
Replace AB -> C with A -> C.
Result: F_min = {A -> C, C -> B, A -> B}
Verify: Is A -> B still redundant?
Remove A -> B:
Compute A⁺ using {A -> C, C -> B}:
A⁺ = {A, C, B}. Contains B. A -> B IS redundant now.
Remove A -> B: F_min = {A -> C, C -> B}
Final minimal cover: {A -> C, C -> B}

SQL databases that support array types (PostgreSQL INTEGER[]JSONB) technically allow violations Of 1NF. This is a pragmatic extension. Use these types when the array is opaque data that you never Need to query or join on individually. If you need to query individual elements or enforce Referential integrity, model them as separate rows.

Definition. A relation RR is in second normal form (2NF) if and only if:

  1. RR is in 1NF, and
  2. No non-prime attribute is partially dependent on any candidate key.

A non-prime attribute is an attribute that does not belong to any candidate key. A partial Dependency exists when a non-prime attribute depends on only a proper subset of a candidate key (rather than the entire key).

4NF violations are rare in practice. They appear when modeling entity-attribute-value Patterns or when a single entity has multiple independent multi-valued attributes. If you see a Table where adding a row requires adding m×nm \times n rows (for mm values of one attribute and nn Values of another), you likely have a 4NF violation.

Fifth Normal Form (5NF) / Project-Join Normal Form (PJNF)

Section titled “Fifth Normal Form (5NF) / Project-Join Normal Form (PJNF)”

Definition. A relation RR is in fifth normal form (5NF) if and only if, for every non-trivial Join dependency JD(R1,R2,,Rn)JD(R_1, R_2, \ldots, R_n) that holds in RREach RiR_i is a superkey of RR.

A join dependency generalizes the concept of lossless-join decomposition to nn relations. A Relation RR satisfies a join dependency JD(R1,R2,,Rn)JD(R_1, R_2, \ldots, R_n) if and only if RR is equal to The natural join of its projections on R1,R2,,RnR_1, R_2, \ldots, R_n:

R=πR1(R)πR2(R)πRn(R)R = \pi_{R_1}(R) \bowtie \pi_{R_2}(R) \bowtie \ldots \bowtie \pi_{R_n}(R)

5NF violations are extremely rare. They arise in ternary (or higher-arity) relationships where the Constraint is inherently multi-way and cannot be decomposed into binary relationships without losing Information.

Classic 5NF example: Supplier-Part-Project
Relation: Supply(supplier, part, project)
Business rule: if a supplier supplies a certain part AND works on a certain project,
then that supplier supplies that part for that project.
This means:
(S₁, P₁) ∈ Supply AND (S₁, J₁) ∈ Supply
IMPLIES (S₁, P₁, J₁) ∈ Supply
Sample data satisfying the rule:
| supplier | part | project |
|----------|------|---------|
| S1 | P1 | J1 |
| S1 | P1 | J2 |
| S1 | P2 | J1 |
Decompose into binary relations:
SP(supplier, part): {(S1,P1), (S1,P2)}
SJ(supplier, project): {(S1,J1), (S1,J2)}
PJ(part, project): {(P1,J1), (P1,J2), (P2,J1)}
Join SP ⋈ SJ ⋈ PJ:
(S1,P1) ⋈ (S1,J1) ⋈ (P1,J1) -> (S1,P1,J1) -- was in original
(S1,P1) ⋈ (S1,J1) ⋈ (P2,J1) -> (S1,P1,J1) -- wait, this needs checking
(S1,P1) ⋈ (S1,J2) ⋈ (P1,J2) -> (S1,P1,J2) -- was in original
(S1,P2) ⋈ (S1,J1) ⋈ (P2,J1) -> (S1,P2,J1) -- was in original
The join SP ⋈ SJ gives: {(S1,P1,J1), (S1,P1,J2), (S1,P2,J1), (S1,P2,J2)}
But (S1,P2,J2) was NOT in the original relation!
This spurious tuple means the decomposition is LOSSY.
The relation is in 5NF because it cannot be decomposed without losing information.
Normal FormEliminatesConditionPractical Relevance
1NFRepeating groups, non-atomic valuesEvery attribute is atomicMandatory for any relational database
2NFPartial dependencies on composite keysNo non-prime attribute partially dependent on any candidate keyOnly matters with composite keys
3NFTransitive dependenciesFor XAX \rightarrow A: XX is a superkey OR AA is primeStandard target for OLTP schemas
BCNFDeterminants that are not superkeys (prime attribute exception)For every non-trivial XYX \rightarrow Y: XX is a superkeyApply when possible; may sacrifice dependency preservation
4NFMulti-valued dependenciesFor every non-trivial XYX \twoheadrightarrow Y: XX is a superkeyRare; occurs with independent multi-valued attributes
5NFJoin dependencies not implied by candidate keysFor every non-trivial JD: each component is a superkeyExtremely rare; mostly theoretical

Worked Example: University Course Registration

Section titled “Worked Example: University Course Registration”

Walk through normalizing a real-world schema from unnormalized to BCNF.

CourseRegistration(student_id, student_name, student_email,
course_id, course_title, course_credits,
instructor_id, instructor_name, instructor_office,
semester, grade)
Sample row:
(1001, "Alice", "alice@univ.edu",
CS101, "Intro to CS", 3,
I5, "Prof Smith", "Room 301",
"Fall 2025", "A")

From the business rules:

student_id -> student_name, student_email
course_id -> course_title, course_credits
instructor_id -> instructor_name, instructor_office
{student_id, course_id, semester} -> grade
course_id, semester -> instructor_id
Compute closure of {student_id, course_id, semester}:
{student_id, course_id, semester}⁺ = {student_id, student_name, student_email,
course_id, course_title, course_credits, instructor_id, instructor_name,
instructor_office, semester, grade} = R
Is it minimal? Remove student_id: {course_id, semester}⁺ = {course_id, course_title,
course_credits, instructor_id, instructor_name, instructor_office, semester} ≠ R.
Remove course_id: {student_id, semester}⁺ = {student_id, student_name, student_email, semester} ≠ R.
Remove semester: {student_id, course_id}⁺ = {student_id, student_name, student_email,
course_id, course_title, course_credits} ≠ R.
Candidate key: {student_id, course_id, semester}
Prime attributes: student_id, course_id, semester
Non-prime attributes: everything else

All attributes are atomic. The relation is in 1NF.

Step 4: Check for 2NF Violations (Partial Dependencies)

Section titled “Step 4: Check for 2NF Violations (Partial Dependencies)”
{student_id, course_id, semester} -> student_name
But student_id -> student_name (partial -- depends on subset of key)
VIOLATES 2NF.
{student_id, course_id, semester} -> course_title, course_credits
But course_id -> course_title, course_credits (partial)
VIOLATES 2NF.
{student_id, course_id, semester} -> instructor_id
But {course_id, semester} -> instructor_id (partial)
VIOLATES 2NF.
{student_id, course_id, semester} -> grade
No subset of the key determines grade. FULL dependency. OK.
R1(student_id, course_id, semester, grade)
FDs: {student_id, course_id, semester} -> grade
Key: {student_id, course_id, semester}
No partial dependencies. In 2NF.
R2(student_id, student_name, student_email)
FDs: student_id -> student_name, student_email
Key: {student_id}
Single-attribute key, automatically in 2NF.
R3(course_id, course_title, course_credits)
FDs: course_id -> course_title, course_credits
Key: {course_id}
Single-attribute key, automatically in 2NF.
R4(course_id, semester, instructor_id)
FDs: {course_id, semester} -> instructor_id
Key: {course_id, semester}
Single non-key attribute. In 2NF.
R5(instructor_id, instructor_name, instructor_office)
FDs: instructor_id -> instructor_name, instructor_office
Key: {instructor_id}
Single-attribute key, automatically in 2NF.

Step 6: Check for 3NF Violations (Transitive Dependencies)

Section titled “Step 6: Check for 3NF Violations (Transitive Dependencies)”
R1: {student_id, course_id, semester} -> grade. No transitive dependency. 3NF.
R2: student_id -> student_name, student_email. No transitive dependency. 3NF.
R3: course_id -> course_title, course_credits. No transitive dependency. 3NF.
R4: {course_id, semester} -> instructor_id. No transitive dependency. 3NF.
R5: instructor_id -> instructor_name, instructor_office. No transitive dependency. 3NF.
All relations are in 3NF.
R1: Only FD is {student_id, course_id, semester} -> grade. Determinant is the key. BCNF.
R2: Only FD is student_id -> student_name, student_email. Determinant is the key. BCNF.
R3: Only FD is course_id -> course_title, course_credits. Determinant is the key. BCNF.
R4: Only FD is {course_id, semester} -> instructor_id. Determinant is the key. BCNF.
R5: Only FD is instructor_id -> instructor_name, instructor_office. Determinant is the key. BCNF.

All five relations are in BCNF. The decomposition is both lossless and dependency-preserving.

Relation: Booking(room_number, date, guest_name, guest_passport)
Business rules:
- A room can have at most one booking per date
- A guest has exactly one passport number
- A guest can book multiple rooms on different dates
FDs:
{room_number, date} -> guest_name, guest_passport
guest_name -> guest_passport
Candidate keys:
{room_number, date} ({room_number, date}⁺ = R)
{room_number, date, guest_name} (obviously a superkey, but not minimal)
Check: is {room_number, guest_name} a candidate key?
{room_number, guest_name}⁺ = {room_number, guest_name, guest_passport} ≠ R (missing date)
Not a superkey.
Only candidate key: {room_number, date}
Prime attributes: room_number, date
Non-prime attributes: guest_name, guest_passport
3NF check:
{room_number, date} -> guest_name: superkey. OK.
{room_number, date} -> guest_passport: superkey. OK.
guest_name -> guest_passport: guest_name is NOT a superkey.
BUT guest_passport is NOT a prime attribute.
VIOLATES 3NF.
Wait -- this also violates 3NF because guest_passport is non-prime.
Let me reconsider: guest_passport depends transitively on the key through guest_name.
{room_number, date} -> guest_name -> guest_passport.
This is a transitive dependency. 3NF violation.
Decomposition:
R1(room_number, date, guest_name) -- key: {room_number, date}
R2(guest_name, guest_passport) -- key: {guest_name}
Both in BCNF. Lossless: R1 ∩ R2 = {guest_name}, guest_name -> guest_passport,
so {guest_name} is a superkey for R2. Lossless.
Dependency-preserving: both FDs are checkable on individual relations.

Decomposition is the mechanism by which normalization is achieved: you replace a single relation With two or more smaller relations. Not every decomposition is correct. A decomposition must satisfy Two properties to be valid.

Definition. A decomposition of relation RR into R1R_1 and R2R_2 is a lossless-join Decomposition if and only if, for every legal instance of RR:

R=R1R2R = R_1 \bowtie R_2

That is, joining the decomposed relations produces exactly the original relation, with no spurious Tuples and no missing tuples.

Theorem. A decomposition of RR into R1R_1 and R2R_2 is lossless if and only if at least one of The following holds:

R1R2R1R_1 \cap R_2 \rightarrow R_1

R1R2R2R_1 \cap R_2 \rightarrow R_2

In words: the common attributes must form a superkey for at least one of the decomposed relations.

R(A, B, C, D) with FDs: {A -> B, B -> C}
Decompose into R1(A, B) and R2(B, C, D):
R1 ∩ R2 = {B}
B -> C holds in F (and thus in R2). But is B a superkey for R2?
B⁺ = {B, C}. B⁺ ≠ R2 = {B, C, D}. B is NOT a superkey for R2.
Is B a superkey for R1? R1 = {A, B}. B⁺ = {B, C} ≠ {A, B}. B is NOT a superkey for R1.
This decomposition is LOSSY.
Decompose into R1(A, B, C) and R2(B, C, D):
R1 ∩ R2 = {B, C}
B -> C holds. Is {B, C} a superkey for R1?
{B, C}⁺ = {B, C}. {B, C}⁺ ≠ {A, B, C}. NOT a superkey for R1.
Is {B, C} a superkey for R2?
{B, C}⁺ = {B, C}. {B, C}⁺ ≠ {B, C, D}. NOT a superkey for R2.
This decomposition is ALSO lossy.
Decompose into R1(A, B, C) and R2(A, D):
R1 ∩ R2 = {A}
A -> B holds. Is {A} a superkey for R1?
A⁺ = {A, B, C} = R1. YES! Lossless. ✓
Decompose into R1(A, B) and R2(A, C, D):
R1 ∩ R2 = {A}
A -> B holds. Is {A} a superkey for R1?
A⁺ = {A, B} = R1. YES! Lossless. ✓
But dependency B -> C is not checkable on R1 or R2 alone.
Not dependency-preserving.

For decompositions into more than two relations, the lossless-join property must be checked Pairwise. A decomposition R1,R2,,RnR_1, R_2, \ldots, R_n is lossless if, when you join them one at a time, Each intermediate join is lossless.

Definition. A decomposition of RR into R1,R2,,RnR_1, R_2, \ldots, R_n is dependency-preserving if and Only if, for every functional dependency XYX \rightarrow Y in the closure of FF (the original set Of FDs), XYRiX \cup Y \subseteq R_i for some ii. Equivalently, the union of the restrictions of FF To each RiR_i is logically equivalent to FF:

(FR1FR2FRn)+=F+(F_{R_1} \cup F_{R_2} \cup \ldots \cup F_{R_n})^+ = F^+

In plain language: every functional dependency from the original relation can be verified by Examining a single decomposed relation. You do not need to join the relations back together to check The constraint.

This is the central practical tension in normalization theory:

Property3NF DecompositionBCNF Decomposition
Lossless-joinAlways achievableAlways achievable
Dependency-preservingAlways achievableNOT always achievable
RedundancyPossible (minor)None

Theorem. For every relation RR with a set of FDs FFThere exists a decomposition of RR into 3NF that is both lossless and dependency-preserving.

Theorem. For every relation RR with a set of FDs FFThere exists a decomposition of RR into BCNF that is lossless, but such a decomposition may NOT be dependency-preserving.

When BCNF loses dependency preservation:
Relation: R(A, B, C)
FDs: {A -> B, B -> C}
3NF decomposition (dependency-preserving):
R1(A, B) with FDs: {A -> B}. 3NF and BCNF.
R2(B, C) with FDs: {B -> C}. 3NF and BCNF.
Both dependencies checkable on individual relations.
Lossless: R1 ∩ R2 = {B}, B -> C, so B is a superkey for R2. ✓
BCNF decomposition (also works here):
Same as above. Both R1 and R2 are already in BCNF.
In this case, BCNF and 3NF coincide.
When BCNF and 3NF differ:
Relation: R(student, course, instructor)
FDs: {{student, course} -> instructor, instructor -> course}
Candidate keys: {student, course} and {student, instructor}
All attributes are prime.
3NF check:
{student, course} -> instructor: superkey. OK.
instructor -> course: instructor is NOT a superkey, but course IS prime. Allowed by 3NF.
In 3NF. ✓
BCNF check:
instructor -> course: instructor is NOT a superkey. VIOLATES BCNF.
BCNF decomposition:
R1(instructor, course) -- FD: instructor -> course. BCNF.
R2(student, instructor) -- no non-trivial FDs. BCNF.
Lost dependency: {student, course} -> instructor.
This dependency requires attributes from both R1 and R2.
It cannot be checked without joining.
3NF approach: keep the original relation.
It is in 3NF. The dependency is preserved.
The tradeoff: instructor -> course means that if Prof Smith teaches
both CS101 and CS201, this information is duplicated across rows.
But the duplication involves only prime attributes, so it is bounded.

The practical rule: always decompose to 3NF. If a relation is not in BCNF, check whether the BCNF Decomposition loses dependency preservation. If it does, and the lost dependency is important for Data integrity, stay in 3NF. If the lost dependency is trivial or can be enforced through Application logic, proceed with BCNF.

In practice, nearly all OLTP schemas target 3NF. Here is the reasoning:

  • 1NF is mandatory. Non-atomic values break relational algebra operations and SQL query semantics.
  • 2NF is almost free. If your keys are single-column surrogate keys, 2NF violations are structurally impossible.
  • 3NF is the sweet spot. It eliminates transitive dependencies (the most common source of real update anomalies) and always admits a dependency-preserving, lossless decomposition.
  • BCNF is a bonus. Apply it when it does not sacrifice dependency preservation. When it does, the redundancy it eliminates involves only prime attributes and is manageable.
  • 4NF and 5NF are almost never encountered in production schemas. When they do arise, the fix is obvious (split independent multi-valued attributes into separate tables).
For each relation in your schema:
1. Does every column contain a single, atomic value? (1NF)
2. Does every non-key column depend on the ENTIRE primary key? (2NF)
3. Does every non-key column depend ONLY on the primary key? (3NF)
4. Is every determinant a superkey? (BCNF)
5. Are there independent multi-valued attributes? (4NF)

Surrogate keys (auto-increment integers, UUIDs) simplify normalization because they make composite Natural keys unnecessary in most cases. A single-column surrogate key means:

  • 2NF violations are impossible (no composite key to have partial dependencies on)
  • Candidate key computation is trivial (the surrogate alone is the primary key)
  • Foreign key references are simpler and more efficient
Without surrogate key:
OrderItem(order_id, product_id, quantity)
Composite key: {order_id, product_id}
2NF violations are possible if product attributes are mixed in.
With surrogate key:
OrderItem(item_id, order_id, product_id, quantity)
Primary key: {item_id}
2NF violations are impossible.
But you still need UNIQUE(order_id, product_id) to prevent duplicates.