Quick answer
Database normalization splits a crowded table into linked tables so each fact is stored once. List the functional dependencies first. Then remove lists from cells (1NF), columns that depend on part of a key (2NF) and columns that depend on other non-key columns (3NF). BCNF adds one rule: every determinant must be a superkey.
Key takeaways
- • Write the functional dependencies from business rules before splitting any table.
- • 1NF removes lists from cells; 2NF and 3NF remove partial and transitive dependencies.
- • A table can pass 3NF and still fail BCNF when a non-key determinant points into a candidate key.
- • BCNF can make a rule unenforceable by keys alone, so some teams stop at 3NF.
- • Declare primary keys, foreign keys and check constraints so Oracle enforces the design.
Table of contents 14 sections
- What Is Database Normalization?
- The Example: An Institute's Enrollment Register
- Which Problems Is This Sheet Hiding?
- Functional Dependencies: Write These Down First
- Step 1: First Normal Form (1NF)
- Step 2: Second Normal Form (2NF)
- Step 3: Third Normal Form (3NF)
- Step 4: Boyce-Codd Normal Form (BCNF)
- The Normal Forms Side by Side
- Building the Final Design in Oracle SQL
- When Should You Stop Normalizing?
- Five Database Normalization Mistakes Students Make
- Where Normalization Fits in PSTA's Oracle Course
- Redraw One Sheet Before Your Next Lab
Database normalization is the process of splitting one crowded table into smaller, linked tables so that every fact is stored in exactly one place. The rules are older than most of the software you use: Edgar F. Codd described first normal form in 1970 and added second and third normal form in 1971, according to the history summarized on Wikipedia. They still decide whether a database stays clean or slowly fills with contradictions.
Most explanations of database normalization show each normal form with a different toy table, so the steps never connect. This walkthrough uses one table from start to finish: a made-up enrollment register of the kind many training institutes and schools in Pakistan keep in Excel. Work through it and you'll be able to:
- Spot insert, update and delete anomalies in a real spreadsheet
- Write the functional dependencies of a table before you change anything
- Move one table through 1NF, 2NF, 3NF and BCNF and explain each split
- Turn the final design into Oracle tables with primary and foreign keys
- Decide when a design is normalized enough and when to stop
What Is Database Normalization?
Database normalization is a set of tests for table design. Each test is called a normal form. A table that passes the first test is in first normal form (1NF), one that also passes the second is in 2NF, and so on. Each level fixes a specific kind of repetition. University courses usually teach it under the heading normalization in DBMS, and the rules are the same whether you use Oracle, MySQL or SQL Server.
Codd's stated goal for the forms beyond 1NF was to free tables from "undesirable insertion, update and deletion dependencies." In everyday terms, you want a design where adding, changing or removing one fact never forces you to touch unrelated facts.
Microsoft's training module on normalization describes the same aim from a practical angle: each table should hold data about one entity, so that a customer's address, for example, is changed in one row instead of fifty. That is the habit this guide builds.
The Example: An Institute's Enrollment Register
Picture a small computer institute that tracks admissions in a single sheet. This is a sample built for teaching, with invented names and fees, but the shape will look familiar if you have ever helped in an admission office.
| student_id | student_name | phone | courses | fees (Rs.) | batches | teachers |
|---|---|---|---|---|---|---|
| 101 | Ayesha | 0300-0000101 | PY01 Python, AC02 AutoCAD | 30,000; 25,000 | PY-NOV, AC-NOV | Ms. Sana; Mr. Fahad |
| 102 | Hamza | 0311-0000102 | PY01 Python | 30,000 | PY-NOV | Ms. Sana |
| 103 | Zainab | 0300-0000101 | OR03 Oracle SQL | 33,000 | OR-DEC | Mr. Fahad |
Notice two details before moving on. Ayesha's row packs two courses into one cell. Zainab shares Ayesha's phone number, which is normal in Pakistan when sisters or cousins register with a parent's mobile. Keep that second detail in mind, because it affects which column can serve as a key.
Which Problems Is This Sheet Hiding?
A sheet like this works for a month. Then the same three problems start appearing, and they have formal names.
| Anomaly | What happens in the register | Real cost |
|---|---|---|
| Update | The Python fee changes, so every row mentioning PY01 must be edited | Miss one row and two fees exist for the same course |
| Insertion | A new Power BI course opens, but no student has joined yet | There's no row to record the course, its fee or its teacher |
| Deletion | Zainab withdraws and her row is removed | The only record that Mr. Fahad teaches the OR-DEC batch disappears with her |
None of these are rare. Database normalization exists to remove them. They're the everyday reason why two staff members quote different fees for the same course, or why nobody can say who taught a batch last year.
Functional Dependencies: Write These Down First
Students often try to normalize by instinct, splitting tables until they "look right." A better method of database normalization starts by listing the functional dependencies. A functional dependency, written A → B, means that if you know A, there is only one possible value of B.
For this register, the rules of the business give us five:
- F1: student_id → student_name, phone
- F2: course_id → course_title, fee
- F3: student_id, course_id → batch_id (a student joins one batch per course)
- F4: batch_id → course_id, start_month, teacher_id (every batch belongs to one course and one teacher)
- F5: teacher_id → teacher_name
Phone is missing from the left side on purpose. Because family members share numbers, phone → student_name is false, so phone can't identify a student. Dependencies come from how the business works, not from the sample rows you happen to have.
Step 1: First Normal Form (1NF)
The first step of database normalization is 1NF. A table is in 1NF when every cell holds a single value, there are no repeating groups, and each row can be identified by a key. Ayesha's "PY01 Python, AC02 AutoCAD" cell breaks the first rule.
The fix is one row per student per course. Never solve it with extra columns like course1, course2 and course3. That layout still repeats a group, and it breaks the day someone joins a fourth course.
| student_id | course_id | student_name | course_title | fee | batch_id | teacher_name |
|---|---|---|---|---|---|---|
| 101 | PY01 | Ayesha | Python | 30,000 | PY-NOV | Ms. Sana |
| 101 | AC02 | Ayesha | AutoCAD | 25,000 | AC-NOV | Mr. Fahad |
| 102 | PY01 | Hamza | Python | 30,000 | PY-NOV | Ms. Sana |
| 103 | OR03 | Zainab | Oracle SQL | 33,000 | OR-DEC | Mr. Fahad |
(The phone, start_month and teacher_id columns are still there; they're hidden here to keep the table readable.) The key is now the pair student_id + course_id. Every cell is atomic, so the table is in 1NF. The anomalies are all still present, though. "Python" and "30,000" already appear twice.
Step 2: Second Normal Form (2NF)
A table is in 2NF when it is in 1NF and no non-key column depends on only part of a candidate key. This rule only matters when a key has more than one column, which is exactly our situation.
Check each dependency against the key student_id + course_id:
- student_name and phone depend on student_id alone (F1). That's a partial dependency.
- course_title and fee depend on course_id alone (F2). Another partial dependency.
- There's a second candidate key hiding here. Because a batch belongs to one course (F4), student_id + batch_id also identifies each row. Measured against that key, start_month and the teacher columns depend on batch_id alone.
Move each group into a table keyed by the column it really depends on:
- STUDENT (student_id, student_name, phone)
- COURSE (course_id, course_title, fee)
- BATCH (batch_id, course_id, start_month, teacher_id, teacher_name)
- ENROLLMENT (student_id, course_id, batch_id)
The fee now lives in one row of COURSE. The update anomaly for fees is gone, and you can add the new Power BI course before anyone enrolls.
Step 3: Third Normal Form (3NF)
A table is in 3NF when it is in 2NF and no non-key column depends on another non-key column. Microsoft's module phrases it as every non-key column being "nontransitively dependent" on the primary key, and it calls 3NF the usual target for transaction databases.
Look at BATCH. Its key is batch_id. It determines teacher_id (F4), and teacher_id determines teacher_name (F5). That chain, batch_id → teacher_id → teacher_name, is a transitive dependency. If Mr. Fahad corrects the spelling of his name, every batch he has ever taught must be edited.
Split the chain into its own table:
- TEACHER (teacher_id, teacher_name)
- BATCH (batch_id, course_id, start_month, teacher_id)
Now check the deletion anomaly from earlier. If Zainab withdraws, you delete her row from ENROLLMENT only. The OR-DEC batch and its teacher stay safely in BATCH.
Step 4: Boyce-Codd Normal Form (BCNF)
Codd and Raymond F. Boyce defined BCNF in 1974. The test is short: for every functional dependency A → B in a table, A must be a superkey, meaning A on its own identifies the row.
Every table so far passes, except one. ENROLLMENT (student_id, course_id, batch_id) has two candidate keys: student_id + course_id, and student_id + batch_id. It also carries F4, batch_id → course_id. batch_id alone doesn't identify an enrollment row, so this dependency breaks BCNF.
Why does the table still pass 3NF? Because course_id is part of a candidate key, and 3NF allows that case. That's the classic gap between the two forms, and it's a favorite exam and viva question.
The BCNF fix is to drop course_id from ENROLLMENT. The course is reachable through BATCH anyway:
- ENROLLMENT (student_id, batch_id)
There's an honest trade-off here. Rule F3, one batch per course for each student, can no longer be enforced by a key, because student_id and course_id no longer sit in the same table. You'd enforce it with a trigger or in the application. Some teams keep the 3NF version for exactly this reason, and that's a defensible choice.
The Normal Forms Side by Side
Here is the whole database normalization path for the register in one table.
| Form | Defined | The question it asks | What it fixed in our register |
|---|---|---|---|
| 1NF | 1970, Codd | Does every cell hold one value? | Split "PY01 Python, AC02 AutoCAD" into separate rows |
| 2NF | 1971, Codd | Does any column depend on only part of a key? | Moved student, course and batch details out |
| 3NF | 1971, Codd | Does any column depend on another non-key column? | Moved teacher names into TEACHER |
| BCNF | 1974, Boyce and Codd | Is every determinant a superkey? | Removed course_id from ENROLLMENT |
Fourth and fifth normal form exist too. Ronald Fagin introduced them in 1977 and 1979. They deal with independent multi-valued facts and complex join rules, and you'll rarely need them in a first project.
Building the Final Design in Oracle SQL
Database normalization on paper is only useful if the database enforces it. Oracle's Data Integrity chapter in the 19c Concepts guide lists the tools: NOT NULL, unique, primary key, foreign key and check constraints. Here are the five tables with those constraints in place:
CREATE TABLE students (
student_id NUMBER(6) CONSTRAINT students_pk PRIMARY KEY,
student_name VARCHAR2(60) NOT NULL,
phone VARCHAR2(15)
);
CREATE TABLE teachers (
teacher_id NUMBER(4) CONSTRAINT teachers_pk PRIMARY KEY,
teacher_name VARCHAR2(60) NOT NULL
);
CREATE TABLE courses (
course_id VARCHAR2(6) CONSTRAINT courses_pk PRIMARY KEY,
course_title VARCHAR2(80) NOT NULL,
fee NUMBER(8) CONSTRAINT courses_fee_ck CHECK (fee >= 0)
);
CREATE TABLE batches (
batch_id VARCHAR2(10) CONSTRAINT batches_pk PRIMARY KEY,
course_id VARCHAR2(6) NOT NULL
CONSTRAINT batches_course_fk REFERENCES courses (course_id),
start_month DATE NOT NULL,
teacher_id NUMBER(4)
CONSTRAINT batches_teacher_fk REFERENCES teachers (teacher_id)
);
CREATE TABLE enrollments (
student_id NUMBER(6)
CONSTRAINT enr_student_fk REFERENCES students (student_id)
ON DELETE CASCADE,
batch_id VARCHAR2(10)
CONSTRAINT enr_batch_fk REFERENCES batches (batch_id),
enrolled_on DATE DEFAULT SYSDATE,
CONSTRAINT enrollments_pk PRIMARY KEY (student_id, batch_id)
);
A few choices are worth explaining:
- No unique constraint on phone. Shared family numbers would make it reject real admissions.
- The composite key is declared out of line. The Oracle 19c SQL Language Reference requires that, and it caps a composite primary key at 32 columns.
- ON DELETE CASCADE on student_id. Deleting a student removes their enrollments too. Leave it out and Oracle blocks the delete while child rows exist, which some offices prefer for audit reasons.
- teacher_id may be NULL. A batch can be planned before a teacher is assigned. Oracle allows nulls in a foreign key column.
To see the original register again, join the tables back together. If joins are new to you, the SQL joins guide with Oracle examples covers each type.
SELECT s.student_name, c.course_title, c.fee, b.start_month, t.teacher_name
FROM enrollments e
JOIN students s ON s.student_id = e.student_id
JOIN batches b ON b.batch_id = e.batch_id
JOIN courses c ON c.course_id = b.course_id
LEFT JOIN teachers t ON t.teacher_id = b.teacher_id
ORDER BY s.student_name;
When Should You Stop Normalizing?
For the database behind an admission form, a billing system or a point-of-sale app, 3NF or BCNF is the normal target. These systems write data all day, and anomalies hurt them most.
Reporting databases are different. Microsoft's module explains that normalized data can need many joins, while denormalized data "can be more efficient to query," especially for read-heavy data warehouses. That's why data warehouses usually use a star schema: one fact table of events, such as fee payments, surrounded by wide dimension tables for students, courses and dates.
So the real skill in database normalization isn't "always normalize." It's knowing which kind of system you're designing. Normalize where data is entered. Denormalize deliberately, and only where data is read for reports. If you're heading toward analytics, our DAX for beginners guide works through a complete sales model.
Five Database Normalization Mistakes Students Make
- Normalizing from sample rows. Three rows can suggest a dependency that the business doesn't have. Ask how the institute or shop actually works.
- Using a name or phone as a key. Names repeat and phones are shared. Use a generated ID.
- Splitting too far. A table with only an ID and one column, created without a dependency reason, just adds joins.
- Forgetting second candidate keys. Our register had two, and missing the second hides both a 2NF and a BCNF problem.
- Stopping at the diagram. If the foreign keys aren't declared in SQL, nothing stops bad data from coming back.
Where Normalization Fits in PSTA's Oracle Course
Table design is the first module of the Oracle Database 19c Masterclass at PSTA, under "SQL Fundamentals & Data Design." Students go on to joins, subqueries and DDL, then PL/SQL triggers, which is where a rule like F3 above can be enforced after a BCNF split.
The course page lists it as a 12-week, beginner-level program, available on campus in Abbottabad, online or as a hybrid. The fee shown there is Rs. 33,000 (listed down from Rs. 45,000), and a new batch starts every month. Later modules cover database administration, RMAN backup and recovery, and performance tuning.
If you're still choosing a database, the Oracle vs MySQL comparison may help. Before a viva or interview, the SQL interview questions for freshers make a good revision list. To compare every program, see all PSTA courses, and when you're ready, the admission page explains how to apply. For quick questions, WhatsApp the office at +92 314 5424399.
Redraw One Sheet Before Your Next Lab
Pick any spreadsheet you already use: a class attendance list, a shop's sales log or your society's fund record. Write its functional dependencies on paper, then take it to 3NF using the steps above. If you can explain why each table exists, you understand database normalization better than a memorized definition ever could.
Frequently asked questions
What is the difference between 3NF and BCNF?
3NF allows a dependency A → B when B is part of a candidate key, even if A is not a key. BCNF removes that exception and requires every determinant to be a superkey. Most tables that pass 3NF also pass BCNF. The difference shows up when a table has two overlapping candidate keys, like the enrollment table in this guide.
Is 3NF enough for most student and business projects?
For most transaction systems, yes. Microsoft describes 3NF as the usual target for OLTP databases. Check for BCNF as well, and if moving to BCNF would stop a business rule from being enforced by a key, it is reasonable to stay at 3NF and document why.
Does database normalization make queries slower?
It can make some read queries slower, because data that used to sit in one table now needs joins. In return, inserts and updates become safer and simpler. That is why systems that record transactions are normalized, while reporting databases and data warehouses are often denormalized into a star schema for faster reads.
What is a functional dependency in simple words?
It is a rule that says one value decides another. If you know a student ID, you know exactly one student name, so student_id → student_name. Dependencies must come from how the business works, not from the few sample rows you have in front of you.
Do I need to learn 4NF and 5NF?
Learn what they are, but you will rarely apply them in early projects. Ronald Fagin introduced them in 1977 and 1979 to handle independent multi-valued facts and complex join rules. For exams, viva and junior developer interviews, a clear grasp of 1NF to BCNF usually matters far more.
Is a phone number or CNIC a good primary key?
Usually not. Families in Pakistan often share one phone number, and students under 18 normally do not have a CNIC yet. Personal identifiers can also change or contain typing mistakes. A generated numeric ID is safer as the primary key. Store the phone or CNIC as an ordinary column instead.
Can I practice normalization in Excel before using a database?
Yes, and it is a good habit. Take a messy sheet, list its functional dependencies on paper, then split it into separate tabs, one per table, each with an ID column. Once the design makes sense, recreate the tabs as tables in Oracle or MySQL and add the key constraints.
Sources & references
- Database normalization (history and normal forms) en.wikipedia.org
- Microsoft Learn: Describe normalization learn.microsoft.com
- Oracle Database 19c Concepts: Data Integrity docs.oracle.com
- Oracle Database 19c SQL Language Reference: Constraints docs.oracle.com
- ACM A.M. Turing Award: E. F. Codd bibliography amturing.acm.org
Last reviewed & updated on by Umar Qureshi. Written by the PSTA team in Abbottabad, Pakistan.
Oracle Database 19c Masterclass: SQL, PL/SQL & Administration
Turn this guide into a career: hands-on training with real projects, online across Pakistan or on-campus in Abbottabad · 12 Weeks.
Comments
No comments yet — ask a question or share your experience.