DBMS 📂 Functional Dependencies & Normalization · 6 of 9 46 min read

Database Normalization Made Simple: 1NF, 2NF & 3NF Solved Step by Step

A complete, worked tutorial that normalizes three classic DBMS assignment tables — Employee–Project, University Classroom Allocation, and Student–Course Enrollment — from an unnormalized "fat table" all the way to Third Normal Form. Each problem identifies the candidate/primary key, then progresses through 1NF (atomic values), 2NF (no partial dependencies), and 3NF (no transitive dependencies)

Section 01

The Problem — One Fat Table That Lies to You

The Receptionist's Notebook
Imagine a company that tracks every employee, every project, every manager, and every phone number in a single notebook page per row. When manager Arvind changes his name, the receptionist must hunt down and edit every single row Arvind appears in. Miss one row, and the database now claims Arvind has two names. Worse — you cannot record a brand-new project until at least one employee is assigned to it, and if the last employee on a project quits, the project quietly vanishes from existence.

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.

Insertion Anomaly
cannot add cleanly
You cannot store a new project, course, or instructor until an unrelated entity (an employee or student) exists to "carry" it. The schema forces fake or null rows.
Update Anomaly
change in many places
One real-world fact (a manager's name, a room's capacity) is duplicated across many rows. Update one and miss another, and the data contradicts itself.
🗑️
Deletion Anomaly
lose facts by accident
Deleting the last employee on a project also deletes the only record that the project ever existed. You lose data you never meant to throw away.
🎯
The Core Goal

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.


Section 02

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.

🔑 ESSENTIAL DEFINITIONS
FD
Functional Dependency (A → B): the value of A uniquely fixes the value of B. "Knowing the EmpID, I know the EmpName." A determines B.
Cand. Key
Candidate Key: a minimal set of attributes that functionally determines every attribute in the relation. The chosen one becomes the Primary Key.
Prime
Prime vs Non-Prime: a prime attribute is part of some candidate key; a non-prime attribute is not. 2NF and 3NF rules are stated in terms of non-prime attributes.
Partial
Partial Dependency: a non-prime attribute depends on only part of a composite key (e.g. EmpID alone, when the key is EmpID + ProjectID). 2NF forbids this.
Transitive
Transitive Dependency: a non-prime attribute depends on another non-prime attribute (Key → X → Y, where X is not a key). 3NF forbids this.
💡
Notation Used Throughout

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…).


Section 03

The Three Normal Forms at a Glance

1️⃣
First Normal Form
atomic values
Every cell holds a single, atomic value — no lists, no repeating groups. The phone column "98770-11111, 98770-22222" breaks this rule and must be split out.
2️⃣
Second Normal Form
no partial dependency
Already in 1NF and every non-prime attribute depends on the whole composite key, not just a slice of it. Relevant only when the key has more than one attribute.
3️⃣
Third Normal Form
no transitive dependency
Already in 2NF and no non-prime attribute depends on another non-prime attribute. Every non-key fact points directly at the key.
🥇
The One-Line Mnemonic

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."


Section 04  ·  Problem A

Employee – Project Assignment

📝
This solves Image 1 and Image 4 together

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)

EmpIDEmpNameEmpPhonesDeptIDDeptName ProjIDProjNameMgrIDMgrNameHrs
E01Rakesh98770-11111, 98770-22222D10FinanceP101Budget AppM15Arvind40
E02Ramesh98770-11178D10FinanceP102Billing SysM18Suman35

The Functional Dependencies

🔗 GIVEN DEPENDENCIES
a
EmpID → EmpName, EmpPhones, DeptID
b
DeptID → DeptName
c
ProjectID → ProjectName, ManagerID
d
ManagerID → ManagerName
e
EmpID, ProjectID → HoursWorked
🔑
Finding the Candidate Key First

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.

❌ Violates 1NF
EmpIDEmpPhones
E0198770-11111, 98770-22222
✅ Atomic (1NF)
EmpIDEmpPhone
E0198770-11111
E0198770-22222
🧮 SCHEMA AFTER 1NF
R1
EmpProject(EmpID, ProjectID, EmpName, DeptID, DeptName, ProjName, MgrID, MgrName, Hrs)
R2
EmpPhone(EmpID, EmpPhone)
🔑
Primary Key after 1NF

EmpProject{EmpID, ProjectID}  •  EmpPhone{EmpID, EmpPhone}

📋 Resulting tables with sample data after 1NF:

EmpProject  PK: EmpID, ProjectID
EmpIDProjectIDEmpNameDeptIDDeptNameProjNameMgrIDMgrNameHrs
E01P101RakeshD10FinanceBudget AppM15Arvind40
E02P102RameshD10FinanceBilling SysM18Suman35
EmpPhone  PK: EmpID, EmpPhone
EmpIDEmpPhone
E0198770-11111
E0198770-22222
E0298770-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.

⚠️ PARTIAL DEPENDENCIES DETECTED
on EmpID
EmpName, DeptID, DeptName depend on EmpID alone — partial.
on ProjID
ProjName, MgrID, MgrName depend on ProjectID alone — partial.
on both
HoursWorked depends on the full key — this one is correct.

Split each partial group into its own relation keyed by the part it truly depends on:

🧮 SCHEMA AFTER 2NF
R1
Employee(EmpID, EmpName, DeptID, DeptName)
R2
Project(ProjectID, ProjName, MgrID, MgrName)
R3
Assignment(EmpID, ProjectID, HoursWorked)
R4
EmpPhone(EmpID, EmpPhone)  (carried from 1NF)
🔑
Primary Key after 2NF

EmployeeEmpID  •  ProjectProjectID  •  Assignment{EmpID, ProjectID}  •  EmpPhone{EmpID, EmpPhone}

📋 Resulting tables with sample data after 2NF:

Employee  PK: EmpID
EmpIDEmpNameDeptIDDeptName
E01RakeshD10Finance
E02RameshD10Finance
Project  PK: ProjectID
ProjectIDProjNameMgrIDMgrName
P101Budget AppM15Arvind
P102Billing SysM18Suman
Assignment  PK: EmpID, ProjectID
EmpIDProjectIDHoursWorked
E01P10140
E02P10235
EmpPhone (unchanged)  PK: EmpID, EmpPhone
EmpIDEmpPhone
E0198770-11111
E0198770-22222
E0298770-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.

⚠️ TRANSITIVE DEPENDENCIES DETECTED
Employee
EmpID → DeptID → DeptName. DeptName hangs off DeptID, not EmpID.
Project
ProjectID → MgrID → MgrName. MgrName hangs off ManagerID, not ProjectID.

Pull each "middle-man" non-key determinant into its own table:

01
Lift out Department
DeptName moves to a Department table keyed by DeptID; Employee keeps DeptID as a foreign key.
02
Lift out Manager
MgrName moves to a Manager table keyed by ManagerID; Project keeps ManagerID as a foreign key.
03
Result: every fact lives once
Renaming a department or manager is now a single-row update anywhere in the system.
✅ FINAL 3NF SCHEMA — EMPLOYEE / PROJECT
1
Employee(EmpID, EmpName, DeptID FK)
2
Department(DeptID, DeptName)
3
Project(ProjectID, ProjName, MgrID FK)
4
Manager(ManagerID, ManagerName)
5
Assignment(EmpID FK, ProjectID FK, HoursWorked)
6
EmpPhone(EmpID FK, EmpPhone)
🔑
Primary Key after 3NF

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):

Employee  PK: EmpID
EmpIDEmpNameDeptID (FK)
E01RakeshD10
E02RameshD10
Department  PK: DeptID
DeptIDDeptName
D10Finance
Project  PK: ProjectID
ProjectIDProjNameMgrID (FK)
P101Budget AppM15
P102Billing SysM18
Manager  PK: ManagerID
ManagerIDManagerName
M15Arvind
M18Suman
Assignment  PK: EmpID, ProjectID
EmpID (FK)ProjectID (FK)HoursWorked
E01P10140
E02P10235
EmpPhone  PK: EmpID, EmpPhone
EmpID (FK)EmpPhone
E0198770-11111
E0198770-22222
E0298770-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)
);

Section 05  ·  Problem B

University Classroom Allocation

The Timetable on a Whiteboard
A university scribbles every class allocation on one whiteboard: class, course, instructor, the instructor's phones, the room, its location and capacity, plus the day and time. When Block A's capacity is re-measured, the figure must be corrected in every single class that meets there. This problem has a twist the others don't — the schedule (Day + TimeSlot) is genuinely part of the key, so we must tease apart "what a class is" from "when it meets."

The Given Table & Dependencies

🔗 ATTRIBUTES & DEPENDENCIES
attrs
ClassID, CourseID, CourseName, InstructorID, InstructorName, Inst_Phones, RoomID, RoomLocation, RoomCapacity, Day, TimeSlot
a
CourseID → CourseName
b
InstructorID → InstructorName, InstructorPhone
c
RoomID → RoomLocation, RoomCapacity
d
ClassID → CourseID, InstructorID, RoomID
e
ClassID, Day, TimeSlot → CourseID, InstructorID, RoomID
🔑
Finding the Candidate Key

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.

🧮 SCHEMA AFTER 1NF
R1
Allocation(ClassID, Day, TimeSlot, CourseID, CourseName, InstrID, InstrName, RoomID, RoomLocation, RoomCapacity)
R2
InstructorPhone(InstructorID, Inst_Phone)
🔑
Primary Key after 1NF

Allocation{ClassID, Day, TimeSlot}  •  InstructorPhone{InstructorID, Inst_Phone}

📋 Resulting tables with sample data after 1NF:

Allocation  PK: ClassID, Day, TimeSlot
ClassIDDayTimeSlotCourseIDCourseNameInstrIDInstrNameRoomIDRoomLocationRoomCap
C01Mon10–11 AMCS101ProgrammingI10Dr. MehtaR12Block A40
C02Tue9–11 AMCS102Data StructI12Dr. VermaR15Block B60
InstructorPhone  PK: InstructorID, Inst_Phone
InstructorIDInst_Phone
I1098760-11111
I1098760-22222
I1299980-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.

⚠️ PARTIAL DEPENDENCY
on ClassID
CourseID, CourseName, InstrID, InstrName, RoomID, RoomLocation, RoomCapacity all depend on ClassID only.
full key
No non-prime attribute depends on the entire {ClassID, Day, TimeSlot}. The schedule itself is the only fact tied to the full key.
🧮 SCHEMA AFTER 2NF
R1
ClassDetails(ClassID, CourseID, CourseName, InstrID, InstrName, RoomID, RoomLocation, RoomCapacity)
R2
Schedule(ClassID, Day, TimeSlot)
R3
InstructorPhone(InstructorID, Inst_Phone)  (from 1NF)
🔑
Primary Key after 2NF

ClassDetailsClassID  •  Schedule{ClassID, Day, TimeSlot}  •  InstructorPhone{InstructorID, Inst_Phone}

📋 Resulting tables with sample data after 2NF:

ClassDetails  PK: ClassID
ClassIDCourseIDCourseNameInstrIDInstrNameRoomIDRoomLocationRoomCap
C01CS101ProgrammingI10Dr. MehtaR12Block A40
C02CS102Data StructI12Dr. VermaR15Block B60
Schedule  PK: ClassID, Day, TimeSlot
ClassIDDayTimeSlot
C01Mon10–11 AM
C02Tue9–11 AM
InstructorPhone (unchanged)  PK: InstructorID, Inst_Phone
InstructorIDInst_Phone
I1098760-11111
I1098760-22222
I1299980-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.

⚠️ TRANSITIVE DEPENDENCIES
Course
ClassID → CourseID → CourseName
Instr.
ClassID → InstructorID → InstructorName
Room
ClassID → RoomID → RoomLocation, RoomCapacity
✅ FINAL 3NF SCHEMA — UNIVERSITY
1
Class(ClassID, CourseID FK, InstructorID FK, RoomID FK)
2
Course(CourseID, CourseName)
3
Instructor(InstructorID, InstructorName)
4
InstructorPhone(InstructorID FK, Inst_Phone)
5
Room(RoomID, RoomLocation, RoomCapacity)
6
Schedule(ClassID FK, Day, TimeSlot)
🔑
Primary Key after 3NF

Class → ClassID  •  Course → CourseID  •  Instructor → InstructorID  •  InstructorPhone → {InstructorID, Inst_Phone}  •  Room → RoomID  •  Schedule → {ClassID, Day, TimeSlot}

📋 Resulting tables with sample data after 3NF:

Class  PK: ClassID
ClassIDCourseID (FK)InstructorID (FK)RoomID (FK)
C01CS101I10R12
C02CS102I12R15
Course  PK: CourseID
CourseIDCourseName
CS101Programming
CS102Data Struct
Instructor  PK: InstructorID
InstructorIDInstructorName
I10Dr. Mehta
I12Dr. Verma
InstructorPhone  PK: InstructorID, Inst_Phone
InstructorID (FK)Inst_Phone
I1098760-11111
I1098760-22222
I1299980-33333
Room  PK: RoomID
RoomIDRoomLocationRoomCapacity
R12Block A40
R15Block B60
Schedule  PK: ClassID, Day, TimeSlot
ClassID (FK)DayTimeSlot
C01Mon10–11 AM
C02Tue9–11 AM

Section 06  ·  Problem C

Student – Course Enrollment

The Enrollment Register
A college register lists, on every enrollment line, the student, their phones, the course, its name, and the full instructor profile — name and department. The moment Dr. Rao moves departments, every row that mentions any course Dr. Rao teaches must be edited by hand. Pull the instructor out once and the move becomes a single keystroke.

The Given Table & Dependencies

SIDStudentNameStudentPhonesCourseIDCourseNameInstrIDInstrNameInst_DeptEnrollDate
S001Anil98765-11111, 98765-22222C102OSI11Dr. SinghCS2025-08-05
S002Ayush99900-33333C101DBMSI10Dr. RaoCS2025-08-01
🔗 GIVEN DEPENDENCIES
a
StudentID → StudentName, Phone
b
CourseID → CourseName, InstructorID, InstructorName, InstructorDept
c
StudentID, CourseID → EnrollmentDate
🔑
Finding the Candidate Key

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.

🧮 SCHEMA AFTER 1NF
R1
Enroll(StudentID, CourseID, StudentName, CourseName, InstrID, InstrName, Inst_Dept, EnrollDate)
R2
StudentPhone(StudentID, StudentPhone)
🔑
Primary Key after 1NF

Enroll{StudentID, CourseID}  •  StudentPhone{StudentID, StudentPhone}

📋 Resulting tables with sample data after 1NF:

Enroll  PK: StudentID, CourseID
StudentIDCourseIDStudentNameCourseNameInstrIDInstrNameInst_DeptEnrollDate
S001C102AnilOSI11Dr. SinghCS2025-08-05
S002C101AyushDBMSI10Dr. RaoCS2025-08-01
StudentPhone  PK: StudentID, StudentPhone
StudentIDStudentPhone
S00198765-11111
S00198765-22222
S00299900-33333

Step 2 → Second Normal Form (2NF)

⚠️ PARTIAL DEPENDENCIES
on SID
StudentName depends on StudentID alone.
on CID
CourseName, InstrID, InstrName, Inst_Dept depend on CourseID alone.
full key
EnrollmentDate depends on the whole key — correct.
🧮 SCHEMA AFTER 2NF
R1
Student(StudentID, StudentName)
R2
Course(CourseID, CourseName, InstrID, InstrName, Inst_Dept)
R3
Enrollment(StudentID, CourseID, EnrollmentDate)
R4
StudentPhone(StudentID, StudentPhone)  (from 1NF)
🔑
Primary Key after 2NF

Student → StudentID  •  Course → CourseID  •  Enrollment → {StudentID, CourseID}  •  StudentPhone → {StudentID, StudentPhone}

📋 Resulting tables with sample data after 2NF:

Student  PK: StudentID
StudentIDStudentName
S001Anil
S002Ayush
Course  PK: CourseID
CourseIDCourseNameInstrIDInstrNameInst_Dept
C102OSI11Dr. SinghCS
C101DBMSI10Dr. RaoCS
Enrollment  PK: StudentID, CourseID
StudentIDCourseIDEnrollmentDate
S001C1022025-08-05
S002C1012025-08-01
StudentPhone (unchanged)  PK: StudentID, StudentPhone
StudentIDStudentPhone
S00198765-11111
S00198765-22222
S00299900-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.

🔎
A Justified Assumption

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.

✅ FINAL 3NF SCHEMA — STUDENT / COURSE
1
Student(StudentID, StudentName)
2
StudentPhone(StudentID FK, StudentPhone)
3
Course(CourseID, CourseName, InstructorID FK)
4
Instructor(InstructorID, InstructorName, Inst_Dept)
5
Enrollment(StudentID FK, CourseID FK, EnrollmentDate)
🔑
Primary Key after 3NF

Student → StudentID  •  StudentPhone → {StudentID, StudentPhone}  •  Course → CourseID  •  Instructor → InstructorID  •  Enrollment → {StudentID, CourseID}

📋 Resulting tables with sample data after 3NF:

Student  PK: StudentID
StudentIDStudentName
S001Anil
S002Ayush
StudentPhone  PK: StudentID, StudentPhone
StudentID (FK)StudentPhone
S00198765-11111
S00198765-22222
S00299900-33333
Course  PK: CourseID
CourseIDCourseNameInstructorID (FK)
C102OSI11
C101DBMSI10
Instructor  PK: InstructorID
InstructorIDInstructorNameInst_Dept
I11Dr. SinghCS
I10Dr. RaoCS
Enrollment  PK: StudentID, CourseID
StudentID (FK)CourseID (FK)EnrollmentDate
S001C1022025-08-05
S002C1012025-08-01

Section 07

Academic vs Industry — Why It Matters Twice

🎓 Academic Lens
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.
🏢 Industry Lens
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.
⚠️
The Trade-Off Engineers Actually Make

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.


Section 08

Golden Rules & Common Traps

🔑 Normalization — Non-Negotiable Rules
1
Find the candidate key before anything else. Every NF rule is phrased relative to the key — you cannot diagnose partial or transitive dependencies until you know what the key is.
2
1NF kills multivalued cells. Any comma-separated list (phones!) becomes its own table with a composite key. Never store a list in one column.
3
2NF only bites composite keys. If your primary key is a single attribute, a 1NF table is automatically in 2NF — there is no "part" of the key to depend on.
4
3NF hunts the "middle-man". Whenever a non-key attribute determines another non-key attribute (DeptID → DeptName, InstructorID → InstructorName), lift that determinant into its own table and leave a foreign key behind.
5
Decomposition must be lossless. The shared attribute you split on must be a key in at least one resulting table, so a join rebuilds the original without inventing or losing rows.
6
Each step inherits the last. 2NF assumes 1NF; 3NF assumes 2NF. Carry forward the tables you already separated (like the phone tables) — don't re-merge them.
You Can Now Defend Every Table

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.