| Lesson 5 | Achieve 3NF |
| Objective | Translate relations into third normal form and verify the resulting design. |
To achieve third normal form, identify all candidate keys and functional dependencies, decompose relations that violate the 3NF rule, and verify the resulting relations and joins. Do not stop after the first split: a newly created table may still contain a dependency that needs attention.
The previous lesson showed how to separate category facts from item facts. This lesson applies the same reasoning to a student registration example containing both partial and transitive dependencies.
Assume the following simplified registration system:
CourseID identifies a particular offering, not a catalog course repeated across terms.Start with one relation whose attributes contain single values, with no repeating groups:
Registration(StudentID, StudentName, CourseID, CourseTitle,
InstructorID, InstructorName, InstructorOffice)
Under these rules, the only candidate key of Registration is (StudentID, CourseID). Neither column alone identifies a registration. The relevant dependencies are:
StudentID → StudentName
CourseID → CourseTitle, InstructorID
InstructorID → InstructorName, InstructorOffice
These are rules for all valid data, not conclusions drawn from a few sample rows. If the business rules change, reassess the keys and dependencies.
Second normal form requires that every non-prime attribute depend on the whole of every candidate key. A non-prime attribute belongs to no candidate key. In Registration, StudentName depends on StudentID alone, while course and instructor facts are determined by CourseID alone. These are dependencies on only part of the composite key.
Separate the student facts, the course facts, and the registration relationship:
Student(StudentID PK, StudentName)
Registration(StudentID PK/FK, CourseID PK/FK)
Course(CourseID PK, CourseTitle, InstructorID,
InstructorName, InstructorOffice)
This is schema notation, not executable SQL. Registration has one composite primary key, (StudentID, CourseID); its two columns are also foreign keys. The Course relation still needs another check.
The dependency chain is now easier to see:
CourseID → InstructorID → InstructorName, InstructorOffice
InstructorID is not a superkey of Course because an instructor can teach several offerings. InstructorName and InstructorOffice are non-prime attributes. Storing those facts in Course would repeat them for each offering taught by that instructor.
The formal 3NF rule: for every nontrivial functional dependency X → A, either X is a superkey or A is a prime attribute. A superkey uniquely identifies a row; a prime attribute belongs to at least one candidate key. A dependency is nontrivial when A is not already part of X.
The instructor dependencies fail both alternatives in Course. Move instructor facts into their own relation and retain InstructorID as the reference.
| Relation | Primary key | Other attributes | Foreign keys |
|---|---|---|---|
| Student | StudentID | StudentName | None |
| Registration | (StudentID, CourseID) | None | StudentID references Student; CourseID references Course |
| Course | CourseID | CourseTitle, InstructorID | InstructorID references Instructor |
| Instructor | InstructorID | InstructorName, InstructorOffice | None |
Under the stated dependencies, each descriptive fact is determined by a key in its own relation. No additional dependencies are assumed among the descriptive attributes. All four relations therefore satisfy 3NF.
Use primary-key and foreign-key constraints to enforce the identifiers and references. Course.InstructorID must be NOT NULL for the rule that every offering has an instructor. Both Registration columns are non-null as components of its primary key. Do not declare names or offices unique unless the business rules require it.
The following illustrative query reconstructs the information for registered student-offering pairs:
SELECT s.StudentID,
s.StudentName,
c.CourseID,
c.CourseTitle,
i.InstructorID,
i.InstructorName,
i.InstructorOffice
FROM Registration AS r
JOIN Student AS s ON s.StudentID = r.StudentID
JOIN Course AS c ON c.CourseID = r.CourseID
JOIN Instructor AS i ON i.InstructorID = c.InstructorID;
The decomposition is lossless under these dependencies: each separation shares an identifier that determines the facts moved into the corresponding parent relation. Each registration joins to one student, one course, and one instructor without generating extra registration combinations. Testing a sample join is useful, but the dependencies explain why reconstruction works for all valid instances.
The design also preserves the stated functional dependencies: student facts can be checked in Student, course facts in Course, and instructor facts in Instructor. The composite key in Registration enforces one row per student-offering pair.
A student or offering with no registrations can now exist independently. Such rows do not appear in this inner-join registration report; that is the report's intended scope, not evidence that their records have been lost.
These checks address the dependencies in this example. Additional requirements, such as multiple instructors per offering or historical office assignments, need additional modeling.
3NF is a useful design baseline, but it does not guarantee the fastest queries or an optimal design for every workload. Measure representative queries, inspect execution plans, and consider appropriate indexes before introducing deliberate redundancy. If denormalization is justified, document which facts are duplicated and how updates keep them consistent.
The next lesson discusses Codd's relational criteria. These concern the capabilities of a relational database system; they are not additional normalization stages for individual tables.