2NF and 3NF
Imagine a university registrar's table. It stores, in one row per (student, course) pair, the enrollment grade. It also stores the student's advisor and the advisor's office. Insert a new course offering with no students yet, and you cannot record it. There is no student key to hang it on. Delete the last student enrolled in a seminar, and you silently lose a fact. You lose the fact that the seminar's professor exists. Update a student's advisor, and complications follow. If that student appears in twelve course rows, you must find and change all twelve. Otherwise the database becomes self-contradictory. These are not hypothetical bugs. They are the textbook symptoms of an under-normalized schema. They occur constantly in real systems built by copying spreadsheets into tables. Second Normal Form (2NF) and Third Normal Form (3NF) are precise, checkable criteria. They diagnose and cure exactly these symptoms. They formalize an intuition every engineer eventually reaches by trial and error. That intuition is "one fact, one place." They turn it into a mechanical procedure. That procedure is driven entirely by Functional Dependencies declared over a schema already in First Normal Form. Rather than relying on taste, a database designer can compute the candidate keys. They can examine the dependency set. They can decide with certainty whether a relation is in 2NF, in 3NF, or neither. If not, they can determine how to decompose it correctly. The stakes are practical. Anomalies caused by poor normalization silently corrupt data over years of application maintenance. This happens because the constraint that would have prevented the corruption was never encoded in the schema. It lived only in a comment or in a developer's head. Normalization pushes that constraint into the structure of the tables themselves. The DBMS then enforces it via primary and foreign keys. This removes the need to trust every future line of application code to remember it. This article defines 2NF and 3NF rigorously in terms of functional dependencies and candidate keys. It gives a step-by-step decomposition algorithm with pseudocode. It traces a full worked example from an unnormalized relation down to 3NF. It is honest about where 3NF still falls short. This motivates the stronger Boyce-Codd Normal Form. It also motivates the general theory of Lossless-Join Decomposition that follows.
- 2NF eliminates partial dependencies: non-key attributes that depend on only part of a composite candidate key.
- 3NF eliminates transitive dependencies: non-key attributes that depend on other non-key attributes rather than directly on a key.
- Both are defined purely in terms of functional dependencies and candidate keys — no table needs to be "eyeballed."
- Violations cause insertion, deletion, and update anomalies; normalization removes them by decomposition.
- The synthesis algorithm (Bernstein-style) computes a 3NF decomposition automatically from a canonical cover.
- 3NF guarantees dependency preservation and lossless join simultaneously — a guarantee BCNF sometimes sacrifices.
2NF and 3NF systematically remove non-key attributes that depend on only part of a key, or on another non-key attribute, so that every fact is stored in exactly one place.
The idea
A relation schema with a set of functional dependencies is in First Normal Form if every attribute holds atomic values — the baseline assumed here. A candidate key of is a minimal set of attributes that functionally determines all attributes of . An attribute is prime if it belongs to some candidate key; otherwise it is non-prime.
Second Normal Form (2NF). is in 2NF if it is in 1NF and every non-prime attribute is fully functionally dependent on every candidate key — meaning no non-prime attribute depends on a proper subset of a candidate key. This condition is vacuous whenever every candidate key has a single attribute, so 2NF violations arise only from composite candidate keys. The forbidden pattern is a partial dependency: where is a proper subset of some candidate key , and is a non-prime attribute not contained in .
Third Normal Form (3NF). is in 3NF if it is in 1NF and, for every nontrivial functional dependency in the closure , at least one of the following holds: 1. is a superkey of (contains a candidate key), or 2. every attribute in is prime (belongs to some candidate key). Condition 2 is the clause that BCNF drops — it is precisely what makes 3NF strictly weaker than Boyce-Codd Normal Form but also what lets 3NF always be achieved without losing any functional dependency. The forbidden pattern that 3NF rules out (beyond what 2NF already rules out) is a transitive dependency: a chain where is a candidate key, is a non-prime attribute not in , is a non-prime attribute not in , and (so the dependency is not simply a key rearrangement). Here depends on only through , not directly. Every 3NF relation is automatically in 2NF, because a partial dependency (with , non-prime) is itself a violation of 3NF's condition 2 (unless happens to be prime, contradicting non-primeness) — so 2NF is logically implied by 3NF. The two forms are presented separately because partial dependencies and transitive dependencies are diagnosed by different visual patterns in practice, and historically 2NF was formulated first as an intermediate stepping stone.
How it works
Both normal forms are checked and repaired by the same two-part recipe: (1) compute the candidate keys from , then (2) scan for the forbidden dependency pattern and decompose whenever one is found. Decomposition means replacing with two or more smaller relations, each holding one determinant and everything it determines, so that the guilty dependency becomes a full (or non-transitive) dependency inside its new, smaller relation.
Step 1 — Find candidate keys. Compute the attribute closure under for candidate subsets , starting from attributes that never appear on the right-hand side of any dependency (they must be in every candidate key), and search minimal supersets whose closure is all of .
Step 2 — Test for 2NF. For each candidate key , and for each proper nonempty subset , compute . If contains a non-prime attribute not in , that is a partial dependency: decompose into (its own relation) and (the rest of , still keyed by ).
Step 3 — Test for 3NF. For each dependency in a minimal cover of , check whether is a superkey or every attribute of is prime. If neither holds, decompose by projecting onto (call it ) and onto (the remainder, still containing as a bridge). In practice, rather than patching one violation at a time, designers use the 3NF synthesis algorithm, which builds a provably correct decomposition directly from a canonical cover in one pass, guaranteeing both properties every good decomposition needs:
- Lossless join: joining the decomposed relations back together, via natural join, reconstructs exactly the original relation — no spurious rows appear.
- Dependency preservation: every functional dependency in can be checked locally, within a single decomposed relation, without needing to join relations back together to verify it.
``` ALGORITHM Synthesize3NF(R, F):
# Step 1: compute a canonical (minimal) cover Fc of F Fc = MinimalCover(F)
# (a) split every RHS into single attributes: alpha -> {b1,...,bn}
# becomes alpha -> b1, ..., alpha -> bn
# (b) remove any extraneous attribute from each LHS
# (attribute a in alpha is extraneous if
# (alpha - a)+ still contains the RHS attribute)
# (c) remove any dependency alpha -> b that is implied by the rest,
# i.e. redundant if alpha+ (computed over Fc - {alpha->b}) contains b
# Step 2: group dependencies that share the same left-hand side group Fc by LHS: L1 -> {b11, b12, ...}, L2 -> {b21, ...}, ... for each group with LHS Li and RHS set Bi: create relation schema Ri = Li union Bi declare Li -> Bi as the FD(s) of Ri Li is a candidate key of Ri (by construction, minimal cover has no redundancy)
# Step 3: ensure a key of the original relation R is represented somewhere if no Ri's attribute set is a superkey of R: compute one candidate key K of R (via attribute closure search) add a new relation schema R_K = K with no non-key attributes
# Step 4: remove redundant relations for each Ri: if Ri's attributes are a subset of some Rj's attributes (i != j): discard Ri return { R1, R2, ..., Rm } # the 3NF decomposition ``` Two subroutines are used as black boxes above and deserve their own statement, since correctness of the whole algorithm rests on them. ``` FUNCTION AttributeClosure(alpha, F): result = alpha repeat: changed = false for each dependency (beta -> gamma) in F: if beta subset of result and gamma not subset of result: result = result union gamma changed = true until not changed return result FUNCTION MinimalCover(F): Fc = SplitRHS(F) # single attribute on each RHS for each dependency d = (alpha -> b) in Fc: for each attribute a in alpha: if b in AttributeClosure(alpha - a, Fc): alpha = alpha - a # a was extraneous; shrink LHS update d to (alpha -> b) for each dependency d = (alpha -> b) in Fc: if b in AttributeClosure(alpha, Fc - {d}): Fc = Fc - {d} # d is redundant given the rest return Fc ```
Complexity & correctness
Computing attribute closure. `AttributeClosure(alpha, F)` runs in time in the naive form shown: each outer pass scans all dependencies, and the closure set grows by at least one attribute per productive pass, so there are at most passes, each costing to scan. With better bookkeeping (a count of unsatisfied LHS attributes per dependency, decremented as attributes are added), this drops to worst case with a much smaller constant, or effectively linear in the total size of using standard graph-reachability tricks — the standard result used across normalization theory.
Computing a minimal cover. `MinimalCover` calls `AttributeClosure` times (once per attribute-removal test, and once per dependency-redundancy test), giving overall complexity polynomial in the size of the input dependency set — specifically in the straightforward implementation. This is entirely tractable because real schemas have and of modest size (tens of dependencies, tens of attributes), never the exponential blow-up seen in candidate-key enumeration for pathological dependency sets.
Finding all candidate keys is, in the worst case, exponential in the number of attributes, since a relation can have an exponential number of minimal keys relative to in adversarially constructed dependency sets. In practice this is a non-issue: the synthesis algorithm above needs only one candidate key of the original (for Step 3), not the full set, and finding one key takes polynomial time by greedily removing attributes from while the closure remains .
Correctness of 2NF decomposition. Suppose is a partial dependency with for candidate key , and non-prime. Splitting into and is lossless because is a key of (by minimality of the dependency) and , so holds — satisfying the lossless-join test: a decomposition into is lossless iff or is in . Here it holds with , so no spurious tuples are introduced when the two projections are joined back via natural join.
Correctness of 3NF synthesis (sketch). The synthesis algorithm's guarantees rest on two facts, both provable from the construction:
- Dependency preservation holds because every dependency in the minimal cover appears, verbatim, inside exactly one synthesized relation — so checking never requires joining relations.
- Lossless join holds because Step 3 guarantees some relation's attributes form a superkey of the original . A classical theorem states that if one relation of a decomposition contains a candidate key of the whole schema, and every dependency in is preserved somewhere in the decomposition, the join of all decomposed relations reconstructs exactly — extra tuples cannot appear because the key relation pins down, functionally, every other attribute value that must appear alongside it, and no dependency forces two different join results for the same key value.
Formally, the join-back proof proceeds by showing for any relation instance satisfying : the direction is immediate (every tuple of projects into every and those projections rejoin to reproduce it), and the direction uses induction on the join order, at each step relying on the fact that the join attributes between the partially-joined relation and the next functionally determine one side (by construction of the synthesis), which is precisely the lossless-join test above applied repeatedly.
Tradeoffs. 3NF is always achievable while preserving both properties simultaneously — this is the celebrated guarantee that Boyce-Codd Normal Form cannot always offer, since eliminating every anomaly BCNF targets can sometimes force giving up dependency preservation. 3NF is therefore the practical "sweet spot" used by most production schemas: some redundancy from condition 2's exception can remain (a prime attribute may still be redundantly repeated), but every dependency stays locally checkable, which matters enormously for constraint enforcement via ordinary primary/foreign keys without triggers spanning multiple tables.
Worked example
Consider an original, unnormalized order-tracking relation:
with functional dependency set :
Step 1 — candidate key. OrderID and ProductID appear only on left-hand sides among the composite dependency, and = all attributes. Removing either attribute fails to reach CustName or ProdName respectively, so is the unique candidate key. All other six attributes are non-prime.
Step 2 — check 2NF. Test proper subsets of :
| Subset | Non-prime attrs beyond ? | Verdict | |
|---|---|---|---|
| {OrderID} | {OrderID, CustName, CustCity} | Yes: CustName, CustCity | Partial dependency |
| {ProductID} | {ProductID, ProdName, ProdCategory} | Yes: ProdName, ProdCategory | Partial dependency |
Two partial dependencies exist, so OrderLine violates 2NF. Decompose:
Each is now checked: 's only dependency, , is full (the key is the whole LHS), so is in 2NF (trivially, since it has no proper key subset smaller than the full key producing extra attributes). and have single-attribute keys, so 2NF is automatic for them.
Step 3 — check 3NF. Inspect with key . Suppose business rules add the realistic dependency (some sales-tax rate) — but here suppose instead a simpler, common real-world addition: . Extend to with dependencies
Now test 3NF on : is a superkey of ? No — . Is prime? No, the only candidate key is . Both 3NF escape clauses fail: this is a transitive dependency . Decompose:
Final 3NF schema:
| Relation | Key | Dependencies held |
|---|---|---|
| (OrderID, CustName, CustCity) | OrderID | OrderID → CustName, CustCity |
| (CustCity, SalesRegion) | CustCity | CustCity → SalesRegion |
| (ProductID, ProdName, ProdCategory) | ProductID | ProductID → ProdName, ProdCategory |
| (OrderID, ProductID, Qty) | {OrderID, ProductID} | OrderID,ProductID → Qty |
Every dependency in the original (plus the added CustCity rule) is preserved locally in exactly one relation, and still carries a superkey of the original relation, so the join of all four relations losslessly reconstructs every valid instance of the original combined table — while now update, insertion, and deletion anomalies involving customer city or product category are impossible, because each fact lives in exactly one row of exactly one relation.
Common pitfalls & traps
| Pitfall | Why it happens | Fix |
|---|---|---|
| Assuming 2NF is meaningful with a single-attribute key | 2NF only restricts composite keys — with one key attribute, no proper nonempty subset exists | Skip straight to checking 3NF when every candidate key is a single attribute |
| Checking 3NF against instead of (or a bad cover) | A schema can look fine against a redundant but hide a violation implied transitively | Always compute a minimal cover, or verify against the full closure |
| Forgetting there may be multiple candidate keys | Designer picks one "primary key" and ignores others, missing that an attribute is prime via a different key | Enumerate all candidate keys before labeling any attribute non-prime |
| Treating 3NF's clause (2) as unnecessary | It is exactly what allows dependency preservation; dropping it silently turns the target into BCNF | Keep the "or RHS is prime" escape clause explicit when justifying a schema is "good enough" |
| Over-normalizing past 3NF for OLTP-style writes | Confusing 3NF with the only acceptable design, even when a controlled BCNF violation is intentional (e.g., for reporting) | Normalize to 3NF as a baseline; only push further after weighing update cost against query cost |
| Losing a dependency during naive one-violation-at-a-time repair | Ad hoc decomposition without recomputing projections can silently drop a constraint | Use the synthesis algorithm from a minimal cover, which is proven dependency-preserving by construction |
Pioneers & history
Origins · 1 minEdgar F. Codd and how this was discovered
Codd introduced the relational model in 1970 as a mathematically grounded alternative to hierarchical and network databases, and over the following four years developed the sequence of normal forms — 1NF, 2NF, 3NF — as formal criteria expressed entirely through functional dependencies and keys. His insistence on defining "good design" through provable properties of dependencies, rather than through informal modeling conventions, is the reason normalization can be taught today as an algorithm rather than an art. Codd received the Turing Award in 1981 for this body of work.
Where this matters
Every mainstream relational database system — PostgreSQL, MySQL, Oracle, SQL Server — is designed on the assumption that schemas fed to it will be normalized at least to 3NF for transactional (OLTP) workloads, and enforcement of foreign keys, unique constraints, and cascading deletes is built around the idea that each table owns exactly one determinant's worth of facts. Entity-Relationship modeling tools and ORM frameworks (Hibernate, Django ORM, ActiveRecord) generate schemas that are 3NF by default whenever relationships are declared with proper foreign keys, implicitly running a version of the synthesis logic in the tool's schema generator. Database design courses and certification exams (and real interview screens for backend engineering roles) routinely test the ability to spot partial and transitive dependencies in a proposed table, because the anomalies they cause — duplicate customer addresses scattered across order rows, orphaned product categories after the last product in that category is deleted — are among the most common data-quality bugs found in production systems. Data warehouse design deliberately reverses course with denormalized star schemas for read-heavy analytics, which only makes sense as a conscious tradeoff once the underlying 3NF-normalized operational schema is understood; and the entire theory here is the direct stepping stone to the stricter guarantees of Boyce-Codd Normal Form and the general machinery of Lossless-Join Decomposition used whenever a schema must be split correctly for any reason, normalization or otherwise.
Ask the tutor: How does the 3NF synthesis algorithm's guarantee break down if you skip computing a minimal cover first?
Why does 2NF only matter when the candidate key is composite?
A partial dependency is defined as a non-prime attribute depending on a proper nonempty subset of a candidate key. If every candidate key has exactly one attribute, no candidate key has a proper nonempty subset to depend on — the empty set determines only itself under any reasonable dependency set. So the definition of a partial dependency can never be satisfied, and every relation with only single-attribute candidate keys is automatically in 2NF, provided it is already in 1NF. This is why textbooks note that 2NF violations only ever arise in relations with composite (multi-attribute) keys, and why many real schemas jump straight from 1NF-thinking to checking for 3NF's transitive dependencies.
Is 3NF always the right stopping point for a production schema?
Not always, but it's an excellent default. 3NF guarantees both lossless-join and dependency-preserving decomposition simultaneously, a combination Boyce-Codd Normal Form cannot always offer, since BCNF's stricter rule sometimes forces dropping dependency preservation to remove a subtler anomaly. For OLTP systems where write correctness and constraint enforcement matter most, 3NF is usually preferred. For analytical or reporting systems, designers often deliberately denormalize past 3NF into star or snowflake schemas, trading some redundancy for query speed, but that decision should be made consciously after the base schema is already 3NF-clean.
How do I find all candidate keys of a relation before checking 2NF or 3NF?
Start with attributes that never appear on the right-hand side of any functional dependency — these are called "left-only" attributes, and they must belong to every candidate key, since nothing else determines them. Compute the closure of this mandatory set; if it already equals all of , that set itself is the unique candidate key. Otherwise, try adding each remaining attribute one at a time (and combinations if needed), testing attribute closure each time, keeping only minimal sets whose closure covers . In practice, most textbook and real-world schemas have very few candidate keys, so this search terminates quickly despite its worst-case exponential nature.
What exactly is a transitive dependency, and why is A → A → B (self-loop) not one?
A transitive dependency is a chain where is a candidate key, and are both non-prime, and does not itself determine back (otherwise would essentially be an alternate key, not a genuinely intermediate non-key attribute). The "transitive" label captures that depends on indirectly, mediated entirely through , rather than directly. A dependency like is trivial (the right-hand side is a subset of the left) and is excluded from all normal form checks by definition, since trivial dependencies hold in every relation and carry no design information.
Can a relation be in 2NF but not in 3NF, and can you show a minimal such example?
Yes — this is the standard motivating case for introducing 3NF after 2NF. Take with candidate key and dependencies and . Since the key has a single attribute, there is no possible partial dependency, so is trivially in 2NF. But has non-prime and non-superkey, and non-prime, so it violates 3NF's two escape clauses — a transitive dependency . This shows 2NF is strictly weaker: it only forbids partial dependencies on composite keys, saying nothing about non-key attributes determining other non-key attributes.
Does normalizing to 3NF ever change the set of valid instances the schema can represent?
No, provided the decomposition is lossless — which the synthesis algorithm guarantees. Losslessness means the natural join of the decomposed relations recovers exactly the same information as the original unnormalized relation, for any instance that satisfies the functional dependencies. What changes is how redundantly that information is physically stored, not what facts can be recorded. The entire point of insisting on lossless-join decomposition, a concept developed fully in Lossless-Join Decomposition, is to make normalization a purely structural improvement with zero loss of expressiveness.
Why is dependency preservation considered important enough to sacrifice full BCNF sometimes?
If a functional dependency is not preserved within any single decomposed relation, then checking whether that dependency still holds after an update requires an expensive join across multiple tables every time a write happens — or worse, the DBMS cannot enforce it via a simple key constraint at all, and the rule silently becomes unenforced application logic. 3NF's extra escape clause (RHS attributes may be prime even when the LHS isn't a superkey) exists specifically to guarantee this never happens: every dependency from the original set is always checkable locally. This is why 3NF, despite being weaker than BCNF, remains the default target for most real production schemas.