Sitemap

Database Management — Week 5

20 min readDec 7, 2025

--

Database Design and Normalization

Press enter or click to view image in full size

Part 1: Understanding Database Design & Anomalies

The Foundation of Database Design

Database design represents the architectural blueprint for how data is structured, stored, and organized within a database system. Think of database design as similar to urban planning for a city, just as city planners determine where residential areas, commercial districts, and transportation networks should be located to create an efficient and livable environment, database designers determine how data elements relate to each other and how they should be organized to support the information needs of an organization. A well-designed database ensures that data is stored efficiently, can be retrieved quickly, and maintains its accuracy and consistency over time.

The importance of logical data structure in database design cannot be overstated. The logical structure refers to how data elements relate to each other independent of physical storage considerations. This includes defining what entities exist in the system, what attributes describe these entities, and how different entities interact through relationships. A sound logical structure makes the database intuitive to use, easy to maintain, and adaptable to changing business requirements. When the logical structure is poorly designed, even the most powerful database management system cannot compensate for the fundamental flaws, leading to systems that are slow, error-prone, and difficult to modify.

The Perils of Data Redundancy

Data redundancy occurs when the same piece of data is stored in multiple places within a database. While some redundancy might be intentionally introduced for performance reasons in advanced database designs, uncontrolled redundancy in transactional systems inevitably leads to serious problems. Consider a university database where student contact information is stored in both the student registration system and the library management system. If a student changes their phone number and updates it in the registration system but not in the library system, the database now contains conflicting information about the same student. This inconsistency undermines the reliability of the entire system.

The problems caused by redundancy manifest through three types of anomalies that corrupt data integrity.

  1. Update anomalies occur when we need to modify the same data in multiple locations, if we forget to update all occurrences, the database becomes inconsistent.
  2. Insertion anomalies happen when we cannot add new data without including unrelated information, for example, we cannot add a new course to the system until at least one student enrolls in it.
  3. Deletion anomalies occur when removing one piece of data unintentionally removes other important information, if we delete the only student enrolled in a particular course, we might lose the course information entirely.

These anomalies collectively represent the fundamental challenges that normalization seeks to address.

Illustrating Poor Database Design

Let’s examine the provided example of a poorly designed student-course table to understand how design flaws manifest in practice. In this table, we see student information, course information, and lecturer information all combined into a single structure.

Press enter or click to view image in full size

The visual clue that something is wrong is the repetition of Dr. Musa’s information across multiple rows. This redundancy is not merely a storage efficiency concern, it creates a brittle system where any change to a lecturer’s email address requires updating every row where that lecturer appears. In a large university with thousands of course enrollments, this could mean hundreds or thousands of updates for a single lecturer’s email change.

The consequences of this poor design extend beyond mere inconvenience. If Dr. Musa decides to change his email address, someone must ensure that every record containing his information is updated consistently. If this update is missed in even one record, queries about Dr. Musa’s courses might return conflicting contact information. Furthermore, this design makes it impossible to add a new lecturer to the system until that lecturer is assigned to teach at least one course, creating a business constraint that doesn’t exist in the real world. Similarly, if all of Dr. Musa’s courses are cancelled and we delete those records, we lose all information about Dr. Musa entirely, even though he remains employed by the university. These practical problems demonstrate why systematic database design is crucial for building reliable information systems.

Part 2: Normalization Theory

Understanding Functional Dependencies

Functional dependencies form the mathematical foundation of normalization theory. A functional dependency exists when the value of one attribute uniquely determines the value of another attribute within a relation. We express this relationship as X → Y, meaning that for any two tuples in the relation, if they have the same value for attribute X, they must have the same value for attribute Y. For example, in a student table, StudentID → StudentName indicates that each student ID determines exactly one student name, if we know the student ID, we can uniquely identify the student’s name.

Understanding functional dependencies is crucial because they reveal the natural relationships between data elements in our system. Some dependencies are desirable and reflect legitimate business rules, such as StudentID → StudentName, which represents the fact that each student has exactly one official name. Other dependencies indicate design problems, particularly when non-key attributes depend on only part of a composite key or on other non-key attributes. By identifying all the functional dependencies in our initial database design, we can systematically reorganize the data to eliminate problematic relationships while preserving the meaningful ones that reflect actual business rules.

First Normal Form (1NF): Establishing Atomicity

First Normal Form represents the most fundamental requirement for relational database design, that all attribute values be atomic. Atomic values cannot be divided into smaller meaningful components while remaining within the relational model. This means we cannot have attributes containing sets, arrays, or other composite structures. For example, an attribute called “Courses” that contains multiple course codes like “CSC309, CSC210” violates 1NF because it embeds a repeating group within a single attribute.

Press enter or click to view image in full size

To transform such data into 1NF, we must reorganize it so that each attribute contains only single values and each record is unique. In the student-courses example, this means creating separate rows for each course a student takes. While this transformation often increases the number of rows in the table, it makes the data amenable to relational operations like filtering and joining. Achieving 1NF also requires ensuring that there’s a primary key that uniquely identifies each row, though at this stage, the primary key might be a composite of multiple attributes. This foundation of atomic values and unique rows establishes the basic structure upon which higher normal forms build.

Press enter or click to view image in full size

Second Normal Form (2NF): Eliminating Partial Dependencies

Second Normal Form addresses the problem of partial dependencies, which occur when non-key attributes depend on only part of a composite primary key. A table is in 2NF if it’s already in 1NF and every non-key attribute is fully functionally dependent on the entire primary key, not just on a subset of it. This means that if a table has a composite primary key consisting of multiple attributes, each non-key attribute must depend on the entire key, not just on one or some of the key attributes.

Consider a student enrollment table with a composite primary key of (StudentID, CourseID) and attributes including StudentName, CourseTitle, and Grade. In this design, StudentName depends only on StudentID (part of the key), and CourseTitle depends only on CourseID (the other part of the key). Only Grade depends on the entire key, both which student and which course we’re considering.

Press enter or click to view image in full size

To achieve 2NF, we must remove these partial dependencies by splitting the table into multiple tables. We would create separate Student and Course tables, leaving only the Grade information in the enrollment table with the composite key. This decomposition ensures that in each table, all non-key attributes depend on the entire primary key.

Press enter or click to view image in full size
Press enter or click to view image in full size
Press enter or click to view image in full size

Third Normal Form (3NF): Removing Transitive Dependencies

Third Normal Form tackles transitive dependencies, which occur when a non-key attribute depends on another non-key attribute rather than directly on the primary key. A table is in 3NF if it’s in 2NF and no transitive dependencies exist between non-key attributes. This means that all non-key attributes must depend directly on the primary key, not indirectly through other non-key attributes. The classic example involves a student table containing StudentID, StudentName, DepartmentID, and DepartmentName. Here, DepartmentName doesn’t depend directly on StudentID but rather on DepartmentID, which in turn depends on StudentID.

Press enter or click to view image in full size

To eliminate this transitive dependency and achieve 3NF, we separate the attributes involved in the transitive relationship into different tables. In our example, we would create a Department table with DepartmentID as the primary key and DepartmentName as an attribute, then include only DepartmentID in the Student table as a foreign key reference. This decomposition ensures that each non-key attribute in every table depends only on the primary key of that table. The result is a design where changes to department information need to be made in only one place, and we can have departments that exist even without any students currently enrolled.

Press enter or click to view image in full size
Press enter or click to view image in full size

Boyce-Codd Normal Form (BCNF): Strengthening Third Normal Form

Boyce-Codd Normal Form represents a stronger version of Third Normal Form that handles certain special cases not addressed by 3NF. A table is in BCNF if for every functional dependency X → Y, X is a superkey (a candidate key or superset of a candidate key). While 3NF allows some dependencies where the determinant is not a candidate key (as long as the dependent is part of a candidate key), BCNF eliminates all such dependencies. In practice, most tables that satisfy 3NF also satisfy BCNF, but there are specific scenarios where the distinction matters.

The difference between 3NF and BCNF becomes important in designs with multiple overlapping candidate keys. For example, consider a table tracking which students can be advised by which professors, with attributes for StudentID, ProfessorID, and Department. If each professor belongs to only one department, but each department has multiple professors, and students can be advised by any professor in their department, we might have complex dependencies that 3NF doesn’t fully resolve. BCNF would require further decomposition to ensure that every determinant is a candidate key. While BCNF represents the theoretically ideal form for eliminating redundancy, practical considerations sometimes lead designers to stop at 3NF for performance reasons.

Press enter or click to view image in full size

Part 3: Practical Oracle SQL Normalization Exercise

Implementing Normalized Tables in Oracle SQL

Let’s now translate our theoretical understanding of normalization into practical implementation using Oracle SQL. We’ll work through the hospital database scenario, starting with the denormalized appointments_raw table that combines patient, doctor, and appointment information. Our first step is to analyze the existing structure and identify the functional dependencies. In this table, we can observe that DoctorName and Specialty are determined by the doctor, PatientName is determined by the patient, and AppointmentDate is determined by the combination of patient and doctor for a specific appointment.

Press enter or click to view image in full size

To create the normalized structure, we’ll build three separate tables that each serve a distinct purpose. The doctors table will store information about healthcare providers with doctor_id as the primary key. The patients table will store patient information with patient_id as the primary key. The appointments table will serve as the connection between doctors and patients, containing foreign keys that reference both tables along with appointment-specific information. This decomposition follows the principles of normalization by separating entities based on their functional dependencies and establishing clear relationships through foreign keys.

Press enter or click to view image in full size

Populating and Querying Normalized Tables

After creating our normalized table structure, we need to populate them with data extracted from our original denormalized table. This process involves careful sequencing: we must first insert data into the independent tables (doctors and patients) before inserting into the dependent table (appointments). For the doctors table, we need to extract unique doctor information from the original data. Similarly, for the patients table, we extract unique patient information. The appointments table then connects these entities using their primary keys.

Once our normalized tables are populated, we can reconstruct the original information using SQL JOIN operations (This will be discussed later). The query that combines data from all three tables demonstrates how normalization doesn’t lose information, it merely reorganizes it in a more structured way. The JOIN operation serves as the mechanism that reassembles the related data when needed. While this approach requires more complex queries for retrieval, it provides significant benefits in terms of data consistency, flexibility, and maintenance. We can now easily update a doctor’s specialty in one place, add new doctors before they have appointments, or delete appointments without losing doctor or patient information.

Comparing Performance and Consistency

When evaluating normalized versus denormalized designs, we must consider both consistency and performance implications. The normalized design excels at maintaining data consistency, when Dr. Musa changes specialty from Cardiology to Internal Medicine, we only need to update one record in the doctors table rather than multiple records in a combined table. This single update automatically propagates to all queries that join the appointments with doctor information, ensuring consistency across the system.

Get Ayoade Akintayo (PhD)’s stories in your inbox

Join Medium for free to get updates from this writer.

However, normalized designs often require more complex queries involving multiple JOIN operations, which can impact performance for certain types of retrievals. In our example, to get the same information that was directly available in the denormalized table, we now need to join three tables together. For large datasets with millions of records, these joins can become computationally expensive. This is why real-world database design often involves thoughtful denormalization in specific areas where read performance is critical, while maintaining normalization in the core transactional structures. The key is finding the right balance based on the specific usage patterns and requirements of the application.

Real-Life Scenario: Hospital Database Normalization

Analyzing the Initial Design

The hospital database scenario presents a classic case of denormalization that creates practical operational problems. In the initial design, a single table contains information about appointments, patients, and doctors intermingled together. The immediate issue is the repetition of doctor information, Dr. Musa’s name and specialty appear multiple times, once for each appointment. This redundancy might seem harmless in a small dataset, but in a real hospital with thousands of appointments, it becomes a significant maintenance burden.

Beyond the obvious redundancy, this design encodes several implicit business constraints that don’t reflect reality. The structure implies that a doctor’s specialty is tied to specific appointments rather than being an inherent property of the doctor. In reality, if Dr. Musa changes specialty, that change should apply to all his current and future appointments. The design also makes it impossible to add a new doctor to the system until that doctor has at least one appointment, which creates operational friction when hiring new medical staff. Furthermore, if we delete all appointments for a particular doctor, we inadvertently lose all record of that doctor’s existence in the system, which could be problematic for credentialing, payroll, and other administrative functions.

Implementing the Normalized Solution

The normalized solution addresses these problems through a logical decomposition of responsibilities. The doctors table becomes the single source of truth for physician information, with each doctor represented exactly once regardless of how many appointments they have. The patients table serves a similar purpose for patient information, storing each patient’s core details in one place. The appointments table then focuses exclusively on capturing the relationship between doctors and patients at specific points in time.

This normalized design aligns with how these entities actually relate in the real world. Doctors exist independently of appointments, patients exist independently of appointments, and appointments represent interactions between doctors and patients. The foreign key relationships enforce referential integrity, ensuring that we cannot create an appointment for a non-existent doctor or patient. At the same time, the design allows doctors to exist without current appointments and patients to exist without current appointments, which matches the operational reality of a hospital where staff and patient rosters change independently of the appointment schedule.

Benefits of the Normalized Structure

The normalized hospital database structure delivers several significant benefits that improve both data quality and system flexibility. Data consistency is dramatically improved because doctor and patient information is stored in exactly one place. When Dr. Musa changes specialties, only one record needs updating, and this change automatically reflects in all future queries about his appointments. This eliminates the risk of inconsistent information appearing in different reports or application screens.

The system becomes more flexible and adaptable to changing business needs. We can now easily add new doctors to the system before they see their first patient, which streamlines the onboarding process. We can maintain historical records of doctors who have left the organization without keeping their appointment records indefinitely. We can also extend the system more easily, for example, adding new attributes to the doctors table (like pager number or office location) without affecting the appointments or patients tables. This modularity makes the system more maintainable and extensible over time, supporting the long-term evolution of the hospital’s information needs.

Hands-on Oracle SQL Exercise

Step-by-Step Normalization Process

Let’s walk through the practical normalization exercise using Oracle SQL Developer/APEX. We begin by creating the denormalized appointments_raw table that serves as our starting point. This table represents how data might be initially captured or imported from an external system. After creating the table, we populate it with sample records that illustrate the redundancy problems we’ve discussed. This gives us a concrete example to work with throughout the normalization process.

CREATE TABLE appointments_raw (
patient_id NUMBER,
patient_name VARCHAR2(100),
doctor_id NUMBER,
doctor_name VARCHAR2(100),
specialty VARCHAR2(100),
appointment_date DATE,
notes VARCHAR2(500)
);
INSERT INTO appointments_raw (
patient_id,
patient_name,
doctor_id,
doctor_name,
specialty,
appointment_date,
notes
) VALUES (
101,
'Ada Johnson',
10,
'Dr. Musa Ahmed',
'Cardiology',
DATE '2024-12-12',
'Follow-up'
);

The next step involves analyzing the functional dependencies within this table. We identify that doctor_name and specialty are determined by the doctor, patient_name is determined by the patient, and appointment_date is determined by the specific appointment. Based on this analysis, we design our normalized table structure: separate tables for doctors, patients, and appointments. We create these tables with appropriate primary keys and foreign key constraints that enforce the relationships between them.

CREATE TABLE doctors (
doctor_id NUMBER PRIMARY KEY,
doctor_name VARCHAR2(100) NOT NULL,
specialty VARCHAR2(100)
);

CREATE TABLE patients (
patient_id NUMBER PRIMARY KEY,
patient_name VARCHAR2(100) NOT NULL
);

CREATE TABLE appointments (
appointment_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
patient_id NUMBER NOT NULL,
doctor_id NUMBER NOT NULL,
appointment_date DATE NOT NULL,
notes VARCHAR2(500),

CONSTRAINT fk_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id),

CONSTRAINT fk_doctor
FOREIGN KEY (doctor_id) REFERENCES doctors(doctor_id)
);

Data Migration and Verification

Populating the normalized tables requires careful data extraction from the original denormalized table. For the doctors table, we use a SELECT DISTINCT query to extract unique combinations of doctor names and specialties. We need to generate unique doctor IDs, which we can do using Oracle’s ROWNUM or a sequence. Similarly, for the patients table, we extract unique patient names and assign patient IDs. The appointments table then uses these generated IDs to establish the relationships between doctors and patients.

INSERT INTO doctors (doctor_id, doctor_name, specialty)
SELECT ROWNUM AS doctor_id,
doctor_name,
specialty
FROM (
SELECT DISTINCT doctor_name, specialty
FROM appointments_raw
);

For Patients table

INSERT INTO patients (patient_id, patient_name)
SELECT ROWNUM AS patient_id,
patient_name
FROM (
SELECT DISTINCT patient_name
FROM appointments_raw
);

For Appointments table

INSERT INTO appointments (
appointment_id,
patient_id,
doctor_id,
appointment_date,
notes
)
SELECT
ROWNUM AS appointment_id,
p.patient_id,
d.doctor_id,
r.appointment_date,
r.notes
FROM appointments_raw r
JOIN patients p
ON r.patient_name = p.patient_name
JOIN doctors d
ON r.doctor_name = d.doctor_name
AND r.specialty = d.specialty;

After populating all three tables, we verify that our normalization hasn’t lost any information by writing a query that joins the normalized tables back together. This query should return exactly the same information that was originally in the appointments_raw table, confirming that our decomposition is lossless.

CREATE OR REPLACE VIEW appointments_denormalized AS
SELECT
p.patient_id,
p.patient_name,
d.doctor_id,
d.doctor_name,
d.specialty,
a.appointment_date,
a.notes
FROM appointments a
JOIN patients p
ON a.patient_id = p.patient_id
JOIN doctors d
ON a.doctor_id = d.doctor_id;

We can also verify that constraints are working correctly by attempting to insert invalid data, for example, trying to create an appointment with a non-existent doctor ID should be rejected by the foreign key constraint.

Comparative Analysis and Discussion

The final step in our hands-on exercise involves comparing the two designs and discussing their relative merits. From a data integrity perspective, the normalized design is clearly superior, it eliminates update, insertion, and deletion anomalies. From a maintenance perspective, the normalized design is easier to manage because changes to doctor or patient information only need to be made in one place.

However, we should also acknowledge the trade-offs. The normalized design requires more complex queries to retrieve combined information, which could impact performance for certain types of reports. There’s also the overhead of maintaining referential integrity through foreign key constraints. This discussion helps students understand that normalization is not an absolute good but rather a design principle that must be balanced against other considerations like performance and complexity. In practice, most production databases use a mixed approach, highly normalized for transactional processing with strategic denormalization for reporting and analytics.

Interactive Class Activity: Normalize Your Campus Database

Activity Implementation

In this group activity, you’ll receive a denormalized “Campus Staff and Courses” table that contains information about instructors, departments, and the courses they teach. Your first task is to carefully examine this table and identify all the functional dependencies. Look for attributes that determine other attributes, particularly focusing on partial dependencies and transitive dependencies. Document these dependencies clearly as they will guide your normalization process.

Once you’ve identified the functional dependencies, begin the step-by-step normalization to Third Normal Form. Start by ensuring the table satisfies First Normal Form, check for atomic values and ensure each record is unique. Then progress to Second Normal Form by eliminating partial dependencies, and finally to Third Normal Form by removing transitive dependencies. At each stage, document how you’re decomposing the original table and what new tables you’re creating. The goal is to end up with a set of tables that minimizes redundancy while preserving all the original information.

Entity Relationship Diagram Development

After normalizing the tables, create an Entity Relationship Diagram that visually represents your normalized structure. Your diagram should clearly show all entities (tables), their attributes, primary keys, foreign keys, and the relationships between tables. Use crow’s foot notation to indicate cardinality (one-to-one, one-to-many, many-to-many). This diagram serves as the blueprint for your normalized database and helps communicate your design decisions to others.

During your presentation to the class, be prepared to explain your normalization decisions and how they address the specific anomalies you identified in the original design. Discuss any trade-offs or design choices you made, particularly if there were multiple valid ways to normalize certain aspects of the data. This presentation practice is valuable professional development, as explaining technical design decisions to stakeholders is a common requirement in real-world database development roles.

Group Assignment: Retail Store Database Normalization

Assignment Overview and Objectives

For this week’s group assignment, you’ll apply normalization principles to a retail store database that currently suffers from significant redundancy. The existing design repeats supplier and category information in every product record, creating the same types of anomalies we’ve discussed throughout this lecture. Your task is to analyze this denormalized structure, identify all functional dependencies and anomalies, and redesign the database to satisfy Third Normal Form.

Your analysis should begin with a thorough examination of the provided denormalized table. Document every functional dependency you find, classifying them as full functional dependencies, partial dependencies, or transitive dependencies. Identify specific examples of update, insertion, and deletion anomalies that could occur with the current design. This analysis will form the foundation for your normalization strategy and will be included in your final report to justify your design decisions.

Implementation and Documentation

After completing your analysis, implement the normalized design in Oracle SQL/APEX. Create all necessary tables with appropriate primary keys, foreign keys, and constraints. Populate each table with at least five realistic sample records that demonstrate how the normalized structure works. Then write and test SQL queries that join these tables to reconstruct the original information, proving that your normalization is lossless.

Your final submission should include complete SQL scripts for table creation and data insertion, screenshots of your query results, and a comprehensive explanation of how normalization improved the design. Specifically, explain how your new design eliminates each type of anomaly present in the original structure. This assignment will demonstrate your ability to not only perform normalization mechanically but also to understand and articulate why normalization matters in practical database development.

Reflection Question

The Long-Term Importance of Normalization

Why is normalization essential for long-term database maintenance, and what real-world risks might occur if an organization skips this process? Normalization is fundamentally about building a resilient foundation for data management that can withstand the test of time. Databases are rarely static, they evolve as business requirements change, regulations shift, and organizations grow. A normalized design provides the flexibility to adapt to these changes without requiring massive restructuring or causing data corruption.

The risks of skipping normalization manifest in several ways as systems age. Data inconsistency becomes increasingly problematic as multiple applications and reporting systems rely on the database. Inconsistent data leads to conflicting business intelligence, poor decision-making, and operational errors. Maintenance overhead grows exponentially as redundant data requires coordinated updates across multiple locations. System brittleness increases, making it difficult to modify the database without breaking existing functionality. Ultimately, denormalized databases become so difficult to maintain that organizations face the costly choice of either continuing with a flawed system or undertaking a painful migration to a properly designed structure.

Real-World Consequences of Poor Design

In real-world scenarios, the consequences of poor database design can be severe. Financial institutions might generate inaccurate regulatory reports due to inconsistent data, potentially leading to compliance violations and substantial fines. Healthcare organizations might deliver incorrect patient care because of unreliable medical records. E-commerce companies might lose customer trust when order information becomes corrupted. In each case, the root cause often traces back to fundamental design flaws that normalization would have prevented.

Perhaps the most insidious risk is that problems often don’t appear immediately. A poorly designed database might function adequately during initial development and early operation when data volumes are small and business rules are simple. However, as the system scales and evolves, the architectural flaws become increasingly problematic. By the time the issues are recognized, the database may be so embedded in business operations that fixing it requires a major project with significant disruption. This is why investing in proper normalization during initial design, even when it seems unnecessary for immediate needs, represents prudent long-term thinking that pays dividends throughout the system’s lifecycle.

Summary and Key Takeaways

This week we’ve explored one of the most fundamental topics in database management: the principles of normalization and their role in creating robust, maintainable database designs. We began by understanding the problems that normalization solves, the various anomalies that occur when data is structured poorly. We systematically worked through the normal forms from 1NF to BCNF, understanding the specific issues each form addresses and the transformations required to achieve them.

Through practical examples and hands-on exercises, we saw how normalization theory applies to real-world scenarios in education, healthcare, and retail. We learned that normalization is not merely an academic exercise but a practical discipline that directly impacts data quality, system maintainability, and business agility. The process of identifying functional dependencies and reorganizing data to eliminate redundancy creates databases that are more consistent, more flexible, and more reliable over the long term.

As we move forward in this course, remember that normalization represents a set of design principles rather than rigid rules. In practice, database professionals must balance normalization against other considerations like performance requirements and implementation complexity. However, understanding these principles provides the foundation for making informed design decisions that serve both immediate needs and long-term objectives. Whether you become a database administrator, application developer, or data analyst, this understanding of normalization will help you build and work with systems that effectively support organizational information needs.

--

--

Ayoade Akintayo (PhD)
Ayoade Akintayo (PhD)

Computer Science lecturer and AI researcher. I write about ML, DL, and how intelligent systems are shaping the world, from classrooms to real-world applications