E-commerce Database Schema Project
Project Overview
An intermediate-level database project for building a complete e-commerce system. This project covers complex relationships, inventory management, order processing, and payment handling.
Learning Objectives
- Design complex normalized schemas
- Implement multiple relationship types
- Handle transactional data
- Manage inventory and stock
- Process orders and payments
- Implement product variations
- Create reporting queries
Database Schema
Tables Overview
- customers: Customer accounts
- categories: Product categories (hierarchical)
- products: Product catalog
- product_variants: Product variations (size, color, etc.)
- inventory: Stock management
- shopping_carts: Active shopping carts
- cart_items: Items in shopping carts
- orders: Customer orders
- order_items: Items in orders
- payments: Payment transactions
- shipping_addresses: Customer shipping addresses
- reviews: Product reviews
Implementation Steps
Step 1: Create Database
CREATE DATABASE ecommerce_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
USE ecommerce_db;
Step 2: Create Customers Table
CREATE TABLE customers (
customer_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
phone VARCHAR(20),
date_of_birth DATE,
customer_status ENUM('active', 'inactive', 'suspended') DEFAULT 'active',
newsletter_subscribed BOOLEAN DEFAULT FALSE,
total_orders INT UNSIGNED DEFAULT 0,
total_spent DECIMAL(12, 2) DEFAULT 0.00,
last_login_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_email (email),
INDEX idx_status (customer_status),
INDEX idx_created (created_at)
) ENGINE=InnoDB;
Step 3: Create Categories Table
CREATE TABLE categories (
category_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
category_name VARCHAR(100) NOT NULL,
slug VARCHAR(100) NOT NULL UNIQUE,
description TEXT,
parent_category_id INT UNSIGNED,
image_url VARCHAR(255),
is_active BOOLEAN DEFAULT TRUE,
display_order INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (parent_category_id) REFERENCES categories(category_id)
ON DELETE SET NULL,
INDEX idx_slug (slug),
INDEX idx_parent (parent_category_id),
INDEX idx_active (is_active),
INDEX idx_order (display_order)
) ENGINE=InnoDB;
Step 4: Create Products Table
CREATE TABLE products (
product_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
product_name VARCHAR(200) NOT NULL,
slug VARCHAR(200) NOT NULL UNIQUE,
sku VARCHAR(100) UNIQUE,
description TEXT,
long_description TEXT,
category_id INT UNSIGNED,
brand VARCHAR(100),
base_price DECIMAL(10, 2) NOT NULL,
sale_price DECIMAL(10, 2),
cost_price DECIMAL(10, 2),
weight DECIMAL(8, 2),
dimensions VARCHAR(50),
is_active BOOLEAN DEFAULT TRUE,
is_featured BOOLEAN DEFAULT FALSE,
requires_shipping BOOLEAN DEFAULT TRUE,
tax_rate DECIMAL(5, 2) DEFAULT 0.00,
view_count INT UNSIGNED DEFAULT 0,
rating_average DECIMAL(3, 2) DEFAULT 0.00,
rating_count INT UNSIGNED DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (category_id) REFERENCES categories(category_id)
ON DELETE SET NULL,
INDEX idx_slug (slug),
INDEX idx_sku (sku),
INDEX idx_category (category_id),
INDEX idx_brand (brand),
INDEX idx_active (is_active),
INDEX idx_featured (is_featured),
INDEX idx_price (base_price),
FULLTEXT INDEX idx_search (product_name, description)
) ENGINE=InnoDB;
Step 5: Create Product Variants Table
CREATE TABLE product_variants (
variant_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
product_id INT UNSIGNED NOT NULL,
sku VARCHAR(100) NOT NULL UNIQUE,
variant_name VARCHAR(100),
attributes JSON, -- {"size": "M", "color": "Blue"}
price_adjustment DECIMAL(10, 2) DEFAULT 0.00,
weight_adjustment DECIMAL(8, 2) DEFAULT 0.00,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (product_id) REFERENCES products(product_id)
ON DELETE CASCADE,
INDEX idx_product (product_id),
INDEX idx_sku (sku),
INDEX idx_active (is_active)
) ENGINE=InnoDB;
Step 6: Create Inventory Table
CREATE TABLE inventory (
inventory_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
product_id INT UNSIGNED,
variant_id INT UNSIGNED,
quantity INT NOT NULL DEFAULT 0,
reserved_quantity INT NOT NULL DEFAULT 0,
available_quantity INT GENERATED ALWAYS AS (quantity - reserved_quantity) STORED,
reorder_level INT DEFAULT 10,
reorder_quantity INT DEFAULT 50,
warehouse_location VARCHAR(100),
last_restocked_at TIMESTAMP NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (product_id) REFERENCES products(product_id)
ON DELETE CASCADE,
FOREIGN KEY (variant_id) REFERENCES product_variants(variant_id)
ON DELETE CASCADE,
INDEX idx_product (product_id),
INDEX idx_variant (variant_id),
INDEX idx_quantity (quantity),
INDEX idx_available (available_quantity),
UNIQUE KEY unique_product_variant (product_id, variant_id)
) ENGINE=InnoDB;
Step 7: Create Shopping Carts Table
CREATE TABLE shopping_carts (
cart_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
customer_id INT UNSIGNED,
session_id VARCHAR(100),
status ENUM('active', 'abandoned', 'converted') DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
expires_at TIMESTAMP NULL,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
ON DELETE CASCADE,
INDEX idx_customer (customer_id),
INDEX idx_session (session_id),
INDEX idx_status (status),
INDEX idx_expires (expires_at)
) ENGINE=InnoDB;
Step 8: Create Cart Items Table
CREATE TABLE cart_items (
cart_item_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
cart_id INT UNSIGNED NOT NULL,
product_id INT UNSIGNED NOT NULL,
variant_id INT UNSIGNED,
quantity INT UNSIGNED NOT NULL DEFAULT 1,
unit_price DECIMAL(10, 2) NOT NULL,
added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (cart_id) REFERENCES shopping_carts(cart_id)
ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(product_id)
ON DELETE CASCADE,
FOREIGN KEY (variant_id) REFERENCES product_variants(variant_id)
ON DELETE CASCADE,
INDEX idx_cart (cart_id),
INDEX idx_product (product_id),
INDEX idx_variant (variant_id)
) ENGINE=InnoDB;
Step 9: Create Shipping Addresses Table
CREATE TABLE shipping_addresses (
address_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
customer_id INT UNSIGNED NOT NULL,
address_label VARCHAR(50),
recipient_name VARCHAR(100) NOT NULL,
phone VARCHAR(20) NOT NULL,
address_line1 VARCHAR(255) NOT NULL,
address_line2 VARCHAR(255),
city VARCHAR(100) NOT NULL,
state VARCHAR(100),
postal_code VARCHAR(20) NOT NULL,
country VARCHAR(100) NOT NULL,
is_default BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
ON DELETE CASCADE,
INDEX idx_customer (customer_id),
INDEX idx_default (is_default)
) ENGINE=InnoDB;
Step 10: Create Orders Table
CREATE TABLE orders (
order_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
order_number VARCHAR(50) NOT NULL UNIQUE,
customer_id INT UNSIGNED NOT NULL,
shipping_address_id INT UNSIGNED,
order_status ENUM('pending', 'processing', 'shipped', 'delivered', 'cancelled', 'refunded') DEFAULT 'pending',
payment_status ENUM('pending', 'paid', 'failed', 'refunded') DEFAULT 'pending',
subtotal DECIMAL(12, 2) NOT NULL,
tax_amount DECIMAL(12, 2) DEFAULT 0.00,
shipping_cost DECIMAL(12, 2) DEFAULT 0.00,
discount_amount DECIMAL(12, 2) DEFAULT 0.00,
total_amount DECIMAL(12, 2) NOT NULL,
currency VARCHAR(3) DEFAULT 'USD',
customer_notes TEXT,
internal_notes TEXT,
ip_address VARCHAR(45),
user_agent VARCHAR(255),
ordered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
shipped_at TIMESTAMP NULL,
delivered_at TIMESTAMP NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
ON DELETE RESTRICT,
FOREIGN KEY (shipping_address_id) REFERENCES shipping_addresses(address_id)
ON DELETE SET NULL,
INDEX idx_order_number (order_number),
INDEX idx_customer (customer_id),
INDEX idx_order_status (order_status),
INDEX idx_payment_status (payment_status),
INDEX idx_ordered_at (ordered_at)
) ENGINE=InnoDB;
Step 11: Create Order Items Table
CREATE TABLE order_items (
order_item_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
order_id INT UNSIGNED NOT NULL,
product_id INT UNSIGNED NOT NULL,
variant_id INT UNSIGNED,
product_name VARCHAR(200) NOT NULL,
sku VARCHAR(100) NOT NULL,
quantity INT UNSIGNED NOT NULL,
unit_price DECIMAL(10, 2) NOT NULL,
subtotal DECIMAL(12, 2) NOT NULL,
tax_amount DECIMAL(12, 2) DEFAULT 0.00,
discount_amount DECIMAL(12, 2) DEFAULT 0.00,
total_amount DECIMAL(12, 2) NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(order_id)
ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(product_id)
ON DELETE RESTRICT,
FOREIGN KEY (variant_id) REFERENCES product_variants(variant_id)
ON DELETE RESTRICT,
INDEX idx_order (order_id),
INDEX idx_product (product_id)
) ENGINE=InnoDB;
Step 12: Create Payments Table
CREATE TABLE payments (
payment_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
order_id INT UNSIGNED NOT NULL,
payment_method ENUM('credit_card', 'debit_card', 'paypal', 'bank_transfer', 'cash_on_delivery') NOT NULL,
payment_status ENUM('pending', 'completed', 'failed', 'refunded') DEFAULT 'pending',
amount DECIMAL(12, 2) NOT NULL,
currency VARCHAR(3) DEFAULT 'USD',
transaction_id VARCHAR(255),
payment_gateway VARCHAR(50),
card_last_four VARCHAR(4),
paid_at TIMESTAMP NULL,
refunded_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (order_id) REFERENCES orders(order_id)
ON DELETE RESTRICT,
INDEX idx_order (order_id),
INDEX idx_transaction (transaction_id),
INDEX idx_status (payment_status)
) ENGINE=InnoDB;
Step 13: Create Reviews Table
CREATE TABLE reviews (
review_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
product_id INT UNSIGNED NOT NULL,
customer_id INT UNSIGNED NOT NULL,
order_id INT UNSIGNED,
rating TINYINT UNSIGNED NOT NULL CHECK (rating BETWEEN 1 AND 5),
title VARCHAR(200),
comment TEXT,
is_verified_purchase BOOLEAN DEFAULT FALSE,
is_approved BOOLEAN DEFAULT FALSE,
helpful_count INT UNSIGNED DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (product_id) REFERENCES products(product_id)
ON DELETE CASCADE,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
ON DELETE CASCADE,
FOREIGN KEY (order_id) REFERENCES orders(order_id)
ON DELETE SET NULL,
INDEX idx_product (product_id),
INDEX idx_customer (customer_id),
INDEX idx_rating (rating),
INDEX idx_approved (is_approved),
UNIQUE KEY unique_review (product_id, customer_id, order_id)
) ENGINE=InnoDB;
Sample Data
See separate file: sample-data.sql
Common Queries
See separate file: common-queries.sql
Features to Implement
Basic Features
- Product catalog browsing
- Shopping cart management
- Checkout process
- Order placement
- Payment processing
- Order tracking
Intermediate Features
- Product variants (size, color, etc.)
- Inventory management
- Product reviews and ratings
- Wishlist functionality
- Order history
- Multiple shipping addresses
Advanced Features
- Coupon/discount codes
- Product recommendations
- Advanced search and filters
- Stock alerts
- Abandoned cart recovery
- Sales analytics
- Multi-currency support
- Tax calculation by region
Practice Exercises
Exercise 1: Product Management
- Add new products with variants
- Update product inventory
- Calculate product ratings
- Find low-stock products
Exercise 2: Order Processing
- Create order from shopping cart
- Calculate order totals with tax
- Update order status
- Process refunds
Exercise 3: Customer Analytics
- Find top customers by spending
- Calculate customer lifetime value
- Identify abandoned carts
- Generate sales reports
Exercise 4: Inventory Management
- Track inventory movements
- Generate reorder reports
- Calculate stock value
- Find bestselling products