The Problem — One Fat Table That Lies to You
That single fat table is convenient to read and a nightmare to maintain. Normalization is the disciplined process of splitting it into honest, non-redundant tables — so each fact lives in exactly one place.
Every problem in this assignment is the same fat table in a different costume — employees, classrooms, students. We will fix all of them with the same three-step recipe: 1NF → 2NF → 3NF, stating the primary key after each step, and explaining the reasoning from both an academic and an industry angle.
Normalization removes redundancy and the three anomalies by ensuring every non-key fact depends on the key, the whole key, and nothing but the key. That single sentence is the heart of 1NF, 2NF, and 3NF.
The Toolkit — Five Words You Must Own
Before touching the tables, lock down the vocabulary. Every decision below is justified using exactly these five ideas.
In every schema below, the underlined attribute(s) form the
PRIMARY KEY, and an attribute tagged
FK is a foreign key pointing to another
table's primary key. Relations are written as TableName(attributes…).
The Three Normal Forms at a Glance
A non-key attribute must depend on the key (1NF: it is in a proper keyed table), the whole key (2NF: no partial dependency), and nothing but the key (3NF: no transitive dependency). "So help me Codd."
Employee – Project Assignment
Both uploaded company tables share the identical attributes and functional dependencies — only the sample rows differ. One worked solution covers both.
The Given Table (Unnormalized)
| EmpID | EmpName | EmpPhones | DeptID | DeptName | ProjID | ProjName | MgrID | MgrName | Hrs |
|---|---|---|---|---|---|---|---|---|---|
| E01 | Rakesh | 98770-11111, 98770-22222 | D10 | Finance | P101 | Budget App | M15 | Arvind | 40 |
| E02 | Ramesh | 98770-11178 | D10 | Finance | P102 | Billing Sys | M18 | Suman | 35 |
The Functional Dependencies
Hours depend on both EmpID and ProjectID (FD e). EmpID pulls in the whole employee/department side; ProjectID pulls in the whole project/manager side. Together {EmpID, ProjectID} determine every attribute — so the candidate (and primary) key of the original table is {EmpID, ProjectID}. Prime attributes: EmpID, ProjectID. Everything else is non-prime.
Step 1 → First Normal Form (1NF)
The only violation of 1NF is the multivalued EmpPhones cell.
We remove the repeating group by pulling phones into their own relation, one phone per row.
Everything else is already atomic.
| EmpID | EmpPhones |
|---|---|
| E01 | 98770-11111, 98770-22222 |
| EmpID | EmpPhone |
|---|---|
| E01 | 98770-11111 |
| E01 | 98770-22222 |
EmpProject → {EmpID, ProjectID} • EmpPhone → {EmpID, EmpPhone}
📋 Resulting tables with sample data after 1NF:
| EmpID | ProjectID | EmpName | DeptID | DeptName | ProjName | MgrID | MgrName | Hrs |
|---|---|---|---|---|---|---|---|---|
| E01 | P101 | Rakesh | D10 | Finance | Budget App | M15 | Arvind | 40 |
| E02 | P102 | Ramesh | D10 | Finance | Billing Sys | M18 | Suman | 35 |
| EmpID | EmpPhone |
|---|---|
| E01 | 98770-11111 |
| E01 | 98770-22222 |
| E02 | 98770-11178 |
Step 2 → Second Normal Form (2NF)
The key is composite: {EmpID, ProjectID}. Now hunt for partial dependencies — non-prime attributes leaning on only one half of the key.
Split each partial group into its own relation keyed by the part it truly depends on:
Employee → EmpID • Project → ProjectID • Assignment → {EmpID, ProjectID} • EmpPhone → {EmpID, EmpPhone}
📋 Resulting tables with sample data after 2NF:
| EmpID | EmpName | DeptID | DeptName |
|---|---|---|---|
| E01 | Rakesh | D10 | Finance |
| E02 | Ramesh | D10 | Finance |
| ProjectID | ProjName | MgrID | MgrName |
|---|---|---|---|
| P101 | Budget App | M15 | Arvind |
| P102 | Billing Sys | M18 | Suman |
| EmpID | ProjectID | HoursWorked |
|---|---|---|
| E01 | P101 | 40 |
| E02 | P102 | 35 |
| EmpID | EmpPhone |
|---|---|
| E01 | 98770-11111 |
| E01 | 98770-22222 |
| E02 | 98770-11178 |
Step 3 → Third Normal Form (3NF)
Now look inside the 2NF tables for transitive chains — a non-key attribute determining another non-key attribute.
Pull each "middle-man" non-key determinant into its own table:
Employee → EmpID • Department → DeptID • Project → ProjectID • Manager → ManagerID • Assignment → {EmpID, ProjectID} • EmpPhone → {EmpID, EmpPhone}
📋 Resulting tables with sample data after 3NF (each fact now stored once):
| EmpID | EmpName | DeptID (FK) |
|---|---|---|
| E01 | Rakesh | D10 |
| E02 | Ramesh | D10 |
| DeptID | DeptName |
|---|---|
| D10 | Finance |
| ProjectID | ProjName | MgrID (FK) |
|---|---|---|
| P101 | Budget App | M15 |
| P102 | Billing Sys | M18 |
| ManagerID | ManagerName |
|---|---|
| M15 | Arvind |
| M18 | Suman |
| EmpID (FK) | ProjectID (FK) | HoursWorked |
|---|---|---|
| E01 | P101 | 40 |
| E02 | P102 | 35 |
| EmpID (FK) | EmpPhone |
|---|---|
| E01 | 98770-11111 |
| E01 | 98770-22222 |
| E02 | 98770-11178 |
The Same Schema in SQL (Industry Deliverable)
CREATE TABLE Department (
DeptID VARCHAR(10) PRIMARY KEY,
DeptName VARCHAR(50)
);
CREATE TABLE Manager (
ManagerID VARCHAR(10) PRIMARY KEY,
ManagerName VARCHAR(50)
);
CREATE TABLE Employee (
EmpID VARCHAR(10) PRIMARY KEY,
EmpName VARCHAR(50),
DeptID VARCHAR(10) REFERENCES Department(DeptID)
);
CREATE TABLE Project (
ProjectID VARCHAR(10) PRIMARY KEY,
ProjName VARCHAR(50),
ManagerID VARCHAR(10) REFERENCES Manager(ManagerID)
);
CREATE TABLE EmpPhone (
EmpID VARCHAR(10) REFERENCES Employee(EmpID),
EmpPhone VARCHAR(20),
PRIMARY KEY (EmpID, EmpPhone)
);
CREATE TABLE Assignment (
EmpID VARCHAR(10) REFERENCES Employee(EmpID),
ProjectID VARCHAR(10) REFERENCES Project(ProjectID),
HoursWorked INT,
PRIMARY KEY (EmpID, ProjectID)
);
University Classroom Allocation
The Given Table & Dependencies
Nothing in the dependency list determines Day or TimeSlot — so they must belong to the key. ClassID then unfolds course, instructor and room (FD d), which in turn unfold their names and details. Therefore the candidate (primary) key is {ClassID, Day, TimeSlot}. Prime attributes: ClassID, Day, TimeSlot. All others are non-prime.
Step 1 → First Normal Form (1NF)
Inst_Phones is multivalued (98760-11111, 98760-22222).
Pull the phones into a dedicated relation; the rest is atomic.
Allocation → {ClassID, Day, TimeSlot} • InstructorPhone → {InstructorID, Inst_Phone}
📋 Resulting tables with sample data after 1NF:
| ClassID | Day | TimeSlot | CourseID | CourseName | InstrID | InstrName | RoomID | RoomLocation | RoomCap |
|---|---|---|---|---|---|---|---|---|---|
| C01 | Mon | 10–11 AM | CS101 | Programming | I10 | Dr. Mehta | R12 | Block A | 40 |
| C02 | Tue | 9–11 AM | CS102 | Data Struct | I12 | Dr. Verma | R15 | Block B | 60 |
| InstructorID | Inst_Phone |
|---|---|
| I10 | 98760-11111 |
| I10 | 98760-22222 |
| I12 | 99980-33333 |
Step 2 → Second Normal Form (2NF)
The key is {ClassID, Day, TimeSlot}. But every descriptive attribute depends on ClassID alone (via FD d) — a textbook partial dependency on part of the key. Day and TimeSlot contribute nothing to determining course, instructor or room.
ClassDetails → ClassID • Schedule → {ClassID, Day, TimeSlot} • InstructorPhone → {InstructorID, Inst_Phone}
📋 Resulting tables with sample data after 2NF:
| ClassID | CourseID | CourseName | InstrID | InstrName | RoomID | RoomLocation | RoomCap |
|---|---|---|---|---|---|---|---|
| C01 | CS101 | Programming | I10 | Dr. Mehta | R12 | Block A | 40 |
| C02 | CS102 | Data Struct | I12 | Dr. Verma | R15 | Block B | 60 |
| ClassID | Day | TimeSlot |
|---|---|---|
| C01 | Mon | 10–11 AM |
| C02 | Tue | 9–11 AM |
| InstructorID | Inst_Phone |
|---|---|
| I10 | 98760-11111 |
| I10 | 98760-22222 |
| I12 | 99980-33333 |
Step 3 → Third Normal Form (3NF)
Inside ClassDetails, ClassID reaches the descriptive fields only by hopping through CourseID, InstructorID and RoomID — three transitive chains.
Class → ClassID • Course → CourseID • Instructor → InstructorID • InstructorPhone → {InstructorID, Inst_Phone} • Room → RoomID • Schedule → {ClassID, Day, TimeSlot}
📋 Resulting tables with sample data after 3NF:
| ClassID | CourseID (FK) | InstructorID (FK) | RoomID (FK) |
|---|---|---|---|
| C01 | CS101 | I10 | R12 |
| C02 | CS102 | I12 | R15 |
| CourseID | CourseName |
|---|---|
| CS101 | Programming |
| CS102 | Data Struct |
| InstructorID | InstructorName |
|---|---|
| I10 | Dr. Mehta |
| I12 | Dr. Verma |
| InstructorID (FK) | Inst_Phone |
|---|---|
| I10 | 98760-11111 |
| I10 | 98760-22222 |
| I12 | 99980-33333 |
| RoomID | RoomLocation | RoomCapacity |
|---|---|---|
| R12 | Block A | 40 |
| R15 | Block B | 60 |
| ClassID (FK) | Day | TimeSlot |
|---|---|---|
| C01 | Mon | 10–11 AM |
| C02 | Tue | 9–11 AM |
Student – Course Enrollment
The Given Table & Dependencies
| SID | StudentName | StudentPhones | CourseID | CourseName | InstrID | InstrName | Inst_Dept | EnrollDate |
|---|---|---|---|---|---|---|---|---|
| S001 | Anil | 98765-11111, 98765-22222 | C102 | OS | I11 | Dr. Singh | CS | 2025-08-05 |
| S002 | Ayush | 99900-33333 | C101 | DBMS | I10 | Dr. Rao | CS | 2025-08-01 |
The enrollment date needs both who enrolled and in what — so the key is {StudentID, CourseID}. StudentID unfolds the student side, CourseID unfolds the course-and-instructor side, and together they fix the date. Prime: StudentID, CourseID.
Step 1 → First Normal Form (1NF)
StudentPhones is multivalued (98765-11111, 98765-22222).
Extract phones to their own relation.
Enroll → {StudentID, CourseID} • StudentPhone → {StudentID, StudentPhone}
📋 Resulting tables with sample data after 1NF:
| StudentID | CourseID | StudentName | CourseName | InstrID | InstrName | Inst_Dept | EnrollDate |
|---|---|---|---|---|---|---|---|
| S001 | C102 | Anil | OS | I11 | Dr. Singh | CS | 2025-08-05 |
| S002 | C101 | Ayush | DBMS | I10 | Dr. Rao | CS | 2025-08-01 |
| StudentID | StudentPhone |
|---|---|
| S001 | 98765-11111 |
| S001 | 98765-22222 |
| S002 | 99900-33333 |
Step 2 → Second Normal Form (2NF)
Student → StudentID • Course → CourseID • Enrollment → {StudentID, CourseID} • StudentPhone → {StudentID, StudentPhone}
📋 Resulting tables with sample data after 2NF:
| StudentID | StudentName |
|---|---|
| S001 | Anil |
| S002 | Ayush |
| CourseID | CourseName | InstrID | InstrName | Inst_Dept |
|---|---|---|---|---|
| C102 | OS | I11 | Dr. Singh | CS |
| C101 | DBMS | I10 | Dr. Rao | CS |
| StudentID | CourseID | EnrollmentDate |
|---|---|---|
| S001 | C102 | 2025-08-05 |
| S002 | C101 | 2025-08-01 |
| StudentID | StudentPhone |
|---|---|
| S001 | 98765-11111 |
| S001 | 98765-22222 |
| S002 | 99900-33333 |
Step 3 → Third Normal Form (3NF)
In Course, the instructor's name and department do not really depend on the course — they depend on the instructor. So CourseID → InstructorID → (InstructorName, Inst_Dept) is a transitive chain.
FD b bundles the instructor fields under CourseID, but the data confirms the natural rule InstructorID → InstructorName, Inst_Dept (I10 is always Dr. Rao in CS). Recognising this hidden dependency is exactly what 3NF asks of us.
Student → StudentID • StudentPhone → {StudentID, StudentPhone} • Course → CourseID • Instructor → InstructorID • Enrollment → {StudentID, CourseID}
📋 Resulting tables with sample data after 3NF:
| StudentID | StudentName |
|---|---|
| S001 | Anil |
| S002 | Ayush |
| StudentID (FK) | StudentPhone |
|---|---|
| S001 | 98765-11111 |
| S001 | 98765-22222 |
| S002 | 99900-33333 |
| CourseID | CourseName | InstructorID (FK) |
|---|---|---|
| C102 | OS | I11 |
| C101 | DBMS | I10 |
| InstructorID | InstructorName | Inst_Dept |
|---|---|---|
| I11 | Dr. Singh | CS |
| I10 | Dr. Rao | CS |
| StudentID (FK) | CourseID (FK) | EnrollmentDate |
|---|---|---|
| S001 | C102 | 2025-08-05 |
| S002 | C101 | 2025-08-01 |
Academic vs Industry — Why It Matters Twice
| Proves correctness via functional dependencies and candidate keys. |
| Each NF is a theorem with a precise rule (atomicity, partial, transitive). |
| Guarantees lossless join and dependency preservation. |
| Goal: a provably anomaly-free design. |
| Cuts storage and kills update/insert/delete anomalies in production. |
| Foreign keys enforce referential integrity automatically. |
| One fact, one place → cheap, safe updates. |
| OLTP systems normalize; analytics/reporting may denormalize for read speed. |
Normalization optimises for write integrity; it costs extra joins at read time. Transactional systems (banking, enrolment, HR) stay at 3NF/BCNF. Data warehouses deliberately denormalize into star schemas to make dashboards fast. Know why you normalize before you decide how far to go.
Golden Rules & Common Traps
For all three problems, every final relation has a clear primary key, no repeating groups, no partial dependency, and no transitive dependency — the definition of being safely in Third Normal Form. Each real-world fact now lives in exactly one place.