Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Database Design Case Studies
DBMS

Database Design Case Studies

Apply database design principles to real-world scenarios: Library Management, E-Commerce, Hospital Management, Social Media, Banking, and Food Delivery.

Database Design Case Studies

Theory is essential, but the real test of database design skills is applying them to real-world problems.

This chapter presents six case studies. Each one walks through the design process: entities, relationships, ER diagram, relational schema, and key design decisions.


Learning Objectives

After completing this chapter, you will be able to:

  • Design a database schema for any given problem statement.
  • Identify entities, relationships, and constraints from requirements.
  • Choose appropriate keys, data types, and normalization levels.
  • Handle edge cases and design tradeoffs.
  • Answer system design interview questions involving databases.

Case Study 1: Library Management System

Requirements

  • The library has many books. Each book has an ISBN, title, author, publisher, and year.
  • Each book can have multiple copies (e.g., 3 copies of “DBMS Fundamentals”).
  • Members can borrow books. A member has an ID, name, email, and phone.
  • A member can borrow up to 5 books at a time.
  • Each borrowing record tracks the checkout date, due date, and return date.
  • Late returns incur a fine.

Entities

  • Book
  • BookCopy
  • Member
  • Borrowing

ER Diagram (Conceptual)

Book (1) ── Has ── (N) BookCopy
Member (1) ── Borrows ── (N) Borrowing
BookCopy (1) ── Borrowed_In ── (N) Borrowing

Relational Schema

CREATE TABLE Books (
    ISBN VARCHAR(13) PRIMARY KEY,
    Title VARCHAR(200) NOT NULL,
    Author VARCHAR(100),
    Publisher VARCHAR(100),
    Year INT
);

CREATE TABLE BookCopies (
    Copy_ID INT PRIMARY KEY,
    ISBN VARCHAR(13) NOT NULL,
    Location VARCHAR(50),
    Status VARCHAR(20) CHECK (Status IN ('Available', 'Borrowed', 'Damaged')),
    FOREIGN KEY (ISBN) REFERENCES Books(ISBN)
);

CREATE TABLE Members (
    Member_ID INT PRIMARY KEY,
    Name VARCHAR(100) NOT NULL,
    Email VARCHAR(255) UNIQUE,
    Phone VARCHAR(20),
    Join_Date DATE DEFAULT CURRENT_DATE
);

CREATE TABLE Borrowings (
    Borrowing_ID INT PRIMARY KEY,
    Member_ID INT NOT NULL,
    Copy_ID INT NOT NULL,
    Checkout_Date DATE NOT NULL,
    Due_Date DATE NOT NULL,
    Return_Date DATE,
    Fine DECIMAL(10,2) DEFAULT 0,
    FOREIGN KEY (Member_ID) REFERENCES Members(Member_ID),
    FOREIGN KEY (Copy_ID) REFERENCES BookCopies(Copy_ID)
);

Key Design Decisions

  • Separate Book and BookCopy: A book is a title; each physical copy is tracked individually. This allows knowing which specific copy was borrowed.
  • Nullable Return_Date: NULL means the book is still borrowed.
  • Fine column: Calculated and stored when the book is returned.

Case Study 2: E-Commerce Platform

Requirements

  • Customers can browse products and place orders.
  • Each product belongs to a category.
  • An order contains multiple items (products and quantities).
  • Customers have a shipping address and payment information.
  • Orders have statuses: Pending, Shipped, Delivered, Cancelled.

Entities

  • Customer
  • Category
  • Product
  • Order
  • OrderItem

Relational Schema

CREATE TABLE Customers (
    Customer_ID INT PRIMARY KEY,
    Name VARCHAR(100) NOT NULL,
    Email VARCHAR(255) UNIQUE,
    Phone VARCHAR(20),
    Address TEXT,
    Registered_Date DATE DEFAULT CURRENT_DATE
);

CREATE TABLE Categories (
    Category_ID INT PRIMARY KEY,
    Name VARCHAR(100) NOT NULL,
    Parent_Category_ID INT,
    FOREIGN KEY (Parent_Category_ID) REFERENCES Categories(Category_ID)
);

CREATE TABLE Products (
    Product_ID INT PRIMARY KEY,
    Category_ID INT,
    Name VARCHAR(200) NOT NULL,
    Description TEXT,
    Price DECIMAL(10,2) CHECK (Price > 0),
    Stock_Quantity INT DEFAULT 0,
    FOREIGN KEY (Category_ID) REFERENCES Categories(Category_ID)
);

CREATE TABLE Orders (
    Order_ID INT PRIMARY KEY,
    Customer_ID INT NOT NULL,
    Order_Date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    Status VARCHAR(20) CHECK (Status IN ('Pending', 'Shipped', 'Delivered', 'Cancelled')),
    Total DECIMAL(12,2),
    Shipping_Address TEXT,
    FOREIGN KEY (Customer_ID) REFERENCES Customers(Customer_ID)
);

CREATE TABLE Order_Items (
    Order_ID INT,
    Product_ID INT,
    Quantity INT NOT NULL CHECK (Quantity > 0),
    Unit_Price DECIMAL(10,2),
    PRIMARY KEY (Order_ID, Product_ID),
    FOREIGN KEY (Order_ID) REFERENCES Orders(Order_ID),
    FOREIGN KEY (Product_ID) REFERENCES Products(Product_ID)
);

Key Design Decisions

  • Parent_Category_ID: Self-referencing FK for subcategories (clothing → men → shirts).
  • Order_Items composite PK: Ensures a product appears once per order.
  • Unit_Price in Order_Items: Product prices change; the order must store the price at the time of purchase.
  • Separate Orders table: An order is an event; its items are in a child table (normalized).

Case Study 3: Hospital Management System

Requirements

  • Patients are admitted to rooms and treated by doctors.
  • Each patient has a record of diagnoses and treatments.
  • Doctors have specializations and can be assigned to multiple patients.
  • Rooms have a type (ICU, General, Private) and a status.

Entities

  • Patient
  • Doctor
  • Room
  • Appointment
  • Treatment

Relational Schema

CREATE TABLE Patients (
    Patient_ID INT PRIMARY KEY,
    Name VARCHAR(100) NOT NULL,
    DOB DATE,
    Gender CHAR(1),
    Phone VARCHAR(20),
    Address TEXT
);

CREATE TABLE Doctors (
    Doctor_ID INT PRIMARY KEY,
    Name VARCHAR(100) NOT NULL,
    Specialization VARCHAR(100),
    Phone VARCHAR(20)
);

CREATE TABLE Rooms (
    Room_ID INT PRIMARY KEY,
    Room_Type VARCHAR(50) CHECK (Room_Type IN ('ICU', 'General', 'Private')),
    Status VARCHAR(20) CHECK (Status IN ('Available', 'Occupied'))
);

CREATE TABLE Admissions (
    Admission_ID INT PRIMARY KEY,
    Patient_ID INT NOT NULL,
    Room_ID INT,
    Admission_Date DATE NOT NULL,
    Discharge_Date DATE,
    FOREIGN KEY (Patient_ID) REFERENCES Patients(Patient_ID),
    FOREIGN KEY (Room_ID) REFERENCES Rooms(Room_ID)
);

CREATE TABLE Appointments (
    Appointment_ID INT PRIMARY KEY,
    Patient_ID INT NOT NULL,
    Doctor_ID INT NOT NULL,
    Appointment_Date TIMESTAMP NOT NULL,
    Diagnosis TEXT,
    FOREIGN KEY (Patient_ID) REFERENCES Patients(Patient_ID),
    FOREIGN KEY (Doctor_ID) REFERENCES Doctors(Doctor_ID)
);

Key Design Decisions

  • Admissions vs Appointments: Admissions track in-patient stays (room assignment). Appointments track doctor visits (outpatient).
  • Discharge_Date is nullable: NULL means the patient is still admitted.
  • Appointment links Patient and Doctor: The M:N relationship between patients and doctors.

Case Study 4: Social Media Platform

Requirements

  • Users can create posts, like posts, and comment on posts.
  • Users can follow other users.
  • Each post has text content and optional media.
  • Likes and comments are tracked with timestamps.

Entities

  • User
  • Post
  • Like
  • Comment
  • Follow

Relational Schema

CREATE TABLE Users (
    User_ID INT PRIMARY KEY,
    Username VARCHAR(50) UNIQUE NOT NULL,
    Email VARCHAR(255) UNIQUE NOT NULL,
    Display_Name VARCHAR(100),
    Created_At TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE Posts (
    Post_ID INT PRIMARY KEY,
    User_ID INT NOT NULL,
    Content TEXT NOT NULL,
    Media_URL VARCHAR(500),
    Created_At TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (User_ID) REFERENCES Users(User_ID)
);

CREATE TABLE Likes (
    User_ID INT,
    Post_ID INT,
    Created_At TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (User_ID, Post_ID),
    FOREIGN KEY (User_ID) REFERENCES Users(User_ID),
    FOREIGN KEY (Post_ID) REFERENCES Posts(Post_ID)
);

CREATE TABLE Comments (
    Comment_ID INT PRIMARY KEY,
    Post_ID INT NOT NULL,
    User_ID INT NOT NULL,
    Content TEXT NOT NULL,
    Created_At TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (Post_ID) REFERENCES Posts(Post_ID),
    FOREIGN KEY (User_ID) REFERENCES Users(User_ID)
);

CREATE TABLE Follows (
    Follower_ID INT,
    Followee_ID INT,
    Created_At TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (Follower_ID, Followee_ID),
    FOREIGN KEY (Follower_ID) REFERENCES Users(User_ID),
    FOREIGN KEY (Followee_ID) REFERENCES Users(User_ID),
    CHECK (Follower_ID != Followee_ID)
);

Key Design Decisions

  • Likes composite PK: Prevents duplicate likes (a user can like a post only once).
  • Follows self-referential: Both Follower and Followee reference Users.
  • CHECK (Follower_ID != Followee_ID): A user cannot follow themselves.

Case Study 5: Banking System

Requirements

  • Customers can open accounts (Savings, Current, Fixed Deposit).
  • Accounts track balance and transaction history.
  • Transactions are either credit (deposit) or debit (withdrawal).
  • A transaction must be atomic — money is not lost during transfers.

Entities

  • Customer
  • Account
  • Transaction

Relational Schema

CREATE TABLE Customers (
    Customer_ID INT PRIMARY KEY,
    Name VARCHAR(100) NOT NULL,
    Email VARCHAR(255) UNIQUE,
    Phone VARCHAR(20),
    Address TEXT
);

CREATE TABLE Accounts (
    Account_Number BIGINT PRIMARY KEY,
    Customer_ID INT NOT NULL,
    Account_Type VARCHAR(20) CHECK (Account_Type IN ('Savings', 'Current', 'FD')),
    Balance DECIMAL(15,2) DEFAULT 0,
    Created_At TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    Status VARCHAR(20) DEFAULT 'Active',
    FOREIGN KEY (Customer_ID) REFERENCES Customers(Customer_ID)
);

CREATE TABLE Transactions (
    Transaction_ID BIGINT PRIMARY KEY,
    Account_Number BIGINT NOT NULL,
    Transaction_Type VARCHAR(10) CHECK (Transaction_Type IN ('Credit', 'Debit')),
    Amount DECIMAL(15,2) NOT NULL CHECK (Amount > 0),
    Balance_Before DECIMAL(15,2),
    Balance_After DECIMAL(15,2),
    Description VARCHAR(500),
    Transaction_Date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (Account_Number) REFERENCES Accounts(Account_Number)
);

Key Design Decisions

  • Balance_Before and Balance_After: Store the balance at transaction time for audit trail. Never calculate balance by scanning all transactions.
  • Account_Number as BIGINT: Bank account numbers are long (12-16 digits).
  • Transaction_Type + CHECK on Amount > 0: Debits are positive amounts; the application subtracts. This prevents negative amounts.
  • Atomic transfers: A transfer = debit from Account A + credit to Account B, wrapped in a transaction.

Case Study 6: Food Delivery

Requirements

  • Restaurants list menu items with prices.
  • Customers place orders from one restaurant at a time.
  • Orders have items, delivery address, and payment.
  • Delivery partners are assigned to orders.

Entities

  • Restaurant
  • MenuItem
  • Customer
  • Order
  • OrderItem
  • DeliveryPartner
  • Delivery

Relational Schema

CREATE TABLE Restaurants (
    Restaurant_ID INT PRIMARY KEY,
    Name VARCHAR(100) NOT NULL,
    Address TEXT,
    Cuisine_Type VARCHAR(50),
    Rating DECIMAL(2,1) DEFAULT 0
);

CREATE TABLE Menu_Items (
    Item_ID INT PRIMARY KEY,
    Restaurant_ID INT NOT NULL,
    Name VARCHAR(100) NOT NULL,
    Description TEXT,
    Price DECIMAL(10,2) NOT NULL,
    Is_Available BOOLEAN DEFAULT TRUE,
    FOREIGN KEY (Restaurant_ID) REFERENCES Restaurants(Restaurant_ID)
);

CREATE TABLE Customers (
    Customer_ID INT PRIMARY KEY,
    Name VARCHAR(100) NOT NULL,
    Phone VARCHAR(20) NOT NULL,
    Address TEXT
);

CREATE TABLE Delivery_Partners (
    Partner_ID INT PRIMARY KEY,
    Name VARCHAR(100) NOT NULL,
    Phone VARCHAR(20) NOT NULL,
    Vehicle_Type VARCHAR(20),
    Is_Available BOOLEAN DEFAULT TRUE
);

CREATE TABLE Orders (
    Order_ID INT PRIMARY KEY,
    Customer_ID INT NOT NULL,
    Restaurant_ID INT NOT NULL,
    Order_Date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    Status VARCHAR(20) CHECK (Status IN ('Placed', 'Preparing', 'Picked', 'Delivered', 'Cancelled')),
    Total DECIMAL(12,2),
    Delivery_Address TEXT,
    FOREIGN KEY (Customer_ID) REFERENCES Customers(Customer_ID),
    FOREIGN KEY (Restaurant_ID) REFERENCES Restaurants(Restaurant_ID)
);

CREATE TABLE Order_Items (
    Order_ID INT,
    Item_ID INT,
    Quantity INT NOT NULL,
    Unit_Price DECIMAL(10,2),
    PRIMARY KEY (Order_ID, Item_ID),
    FOREIGN KEY (Order_ID) REFERENCES Orders(Order_ID),
    FOREIGN KEY (Item_ID) REFERENCES Menu_Items(Item_ID)
);

CREATE TABLE Deliveries (
    Delivery_ID INT PRIMARY KEY,
    Order_ID INT UNIQUE NOT NULL,
    Partner_ID INT,
    Picked_At TIMESTAMP,
    Delivered_At TIMESTAMP,
    FOREIGN KEY (Order_ID) REFERENCES Orders(Order_ID),
    FOREIGN KEY (Partner_ID) REFERENCES Delivery_Partners(Partner_ID)
);

Key Design Decisions

  • Order_Items has Unit_Price: Menu prices change; the order price is recorded at order time.
  • Deliveries has UNIQUE on Order_ID: One order has one delivery.
  • Nullable Partner_ID: An order can be placed before a delivery partner is assigned.
  • Delivery_Partners.Is_Available: Bit flag for the assignment algorithm.

General Design Principles

PrincipleWhy
Start with normalizationEliminate redundancy first; denormalize later if needed
Use surrogate keysAuto-increment INT/BIGINT; avoid natural keys
Always add timestampscreated_at, updated_at on every table
Store price at transaction timePrices change; the historical price matters
Use CHECK constraintsEnforce business rules at the database level
Index foreign keysEvery FK should have an index for join performance
Never store calculated valuesExcept for audit/historical purposes
Plan for soft deletesAdd is_deleted or deleted_at column
Document your schemaComments on tables and columns save months of confusion

Interview Deep Dive

Q: In an e-commerce database, why do you store Unit_Price in Order_Items instead of just referencing the Products table?

A: Because product prices change over time. If a customer buys a product at ₹1000 today and the price increases to ₹1200 next week, the order history must show ₹1000 — the price at the time of purchase. Referencing the Products table would show the current price, not the historical price.

Q: In a banking system, why do you store Balance_Before and Balance_After in the Transactions table?

A: For audit trail and reconciliation. The account’s current balance should always match the last transaction’s Balance_After. This also allows detecting discrepancies — if someone modifies a past transaction, the Balance_Before/After chain breaks, immediately flagging the issue.

Q: How would you design the Likes table for a social media platform with millions of likes per day?

A: The basic design is a composite PK (User_ID, Post_ID). For scale: partition by Post_ID (range-based), use a counter cache on the Posts table (like_count column updated async), and consider a dedicated like service with its own database for write isolation.

Q: Should you use physical or logical deletes in a hospital management system?

A: Logical deletes (soft deletes) are preferred for medical records — patient data must never be permanently deleted for legal and audit reasons. Add a deleted_at TIMESTAMP column. Queries should filter WHERE deleted_at IS NULL. Physical deletes are reserved for non-critical data with clear retention policies.


Key Takeaways

  • Every database design starts with understanding the problem requirements.
  • Identify entities, attributes, and relationships before writing SQL.
  • Use surrogate keys, index foreign keys, and enforce constraints.
  • Store historical prices in order/transaction tables.
  • Plan for audit trails in financial and medical systems.
  • Soft deletes protect data in regulated domains.
  • Normalize first, denormalize only when performance requires it.
  • Six case studies cover the most common real-world database design scenarios.

My Private Notes

Notes are auto-saved locally to this device.