DataBase Design Lesson 27 – Logical Schema Design | Dataplexa
Database Design · Lesson 27

Logical Schema Design

Transform your normalized data structure into a complete database schema with proper constraints, data types, and business rules

From Tables to Schema

Normalization gave us clean tables. But that's just the beginning. Logical schema design takes those tables and turns them into a real database blueprint with data types, constraints, and business rules.

Think of it like this: normalization designs the rooms in your house. Logical schema design picks the materials, adds the plumbing, and defines how everything connects. Without this step, your database will work — but it won't be reliable.

BookVault needs a schema that prevents bad data from entering, enforces business rules automatically, and handles edge cases gracefully. Here's how we build one.

1. Data Types

Choose precise storage for each column

2. Constraints

Add rules that prevent bad data

3. Relationships

Define how tables connect

4. Business Rules

Encode real-world requirements

Choosing Data Types

Every column needs a data type. And this choice matters more than most developers realize.

Pick VARCHAR(255) for everything? Your database gets bloated. Use TEXT for short fields? Queries slow down. Choose DECIMAL for prices but FLOAT for ratings? Now you're thinking like a database architect.

BookVault Column Data Type Why This Choice
customer_id INT AUTO_INCREMENT Sequential IDs, database handles numbering
email VARCHAR(254) Max email length per RFC standard
price DECIMAL(10,2) Exact currency precision, no rounding errors
order_date TIMESTAMP Includes timezone, auto-updates available
status ENUM Limited set of valid values

Common Mistake

Using VARCHAR(255) for everything because "255 is safe." This wastes storage and can impact performance. A book title rarely exceeds 200 characters, but a city name rarely exceeds 50. Size your columns based on real data.

Adding Constraints

Constraints are your database's immune system. They prevent bad data before it infects your system. No constraint means any garbage can get in.

BookVault needs constraints that mirror real business rules. Customers can't have blank emails. Book prices can't be negative. Order dates can't be in the future. Stock quantities can't be less than zero.

-- BookVault customers table with comprehensive constraints
-- Each constraint prevents a specific type of data corruption
CREATE TABLE customers (
    customer_id INT AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(50) NOT NULL CHECK (LENGTH(first_name) >= 2),
    last_name VARCHAR(50) NOT NULL CHECK (LENGTH(last_name) >= 2),
    email VARCHAR(254) NOT NULL UNIQUE CHECK (email LIKE '%@%.%'),
    city VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Query OK, 0 rows affected (0.02 sec)

What just happened?

Each constraint serves a purpose: NOT NULL prevents empty data, UNIQUE prevents duplicate emails, and CHECK validates data format. Try this: attempt to insert a customer with email "invalid" — the database will reject it.

Here's the books table with business-critical constraints:

-- BookVault books table with price and inventory protection
-- Prevents negative prices and invalid stock levels
CREATE TABLE books (
    book_id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(200) NOT NULL CHECK (LENGTH(title) >= 1),
    author_id INT NOT NULL,
    category_id INT NOT NULL,
    price DECIMAL(10,2) NOT NULL CHECK (price >= 0.01),
    stock_qty INT NOT NULL DEFAULT 0 CHECK (stock_qty >= 0),
    published_year INT CHECK (published_year BETWEEN 1000 AND YEAR(CURDATE())),
    FOREIGN KEY (author_id) REFERENCES authors(author_id),
    FOREIGN KEY (category_id) REFERENCES categories(category_id)
);
Query OK, 0 rows affected (0.03 sec)
Data Insight

Constraints prevent 80% of data corruption issues in production systems. The 10 minutes spent adding them saves hours of cleanup later.

Schema Relationships

Foreign keys create the connections between tables. But they do more than just link data — they maintain referential integrity automatically.

Think of foreign keys as database guardrails. You can't create an order for a customer who doesn't exist. You can't delete an author who still has books in the catalog. The database enforces these rules for you.

CUSTOMERS
Attributes
PK customer_id
first_name
last_name
email
ONE
places
MANY
ORDERS
Attributes
PK order_id
FK customer_id
order_date
total_amount
ORDERS
Attributes
PK order_id
customer_id
order_date
ONE
contains
MANY
ORDER_ITEMS
Attributes
PK order_item_id
FK order_id
FK book_id
quantity

Now the orders table with complete relationship definitions:

-- BookVault orders table with foreign key relationships
-- Links customers to their orders with referential integrity
CREATE TABLE orders (
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT NOT NULL,
    order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    status ENUM('pending', 'processing', 'shipped', 'delivered', 'cancelled') 
           DEFAULT 'pending',
    total_amount DECIMAL(12,2) NOT NULL CHECK (total_amount >= 0),
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
        ON DELETE RESTRICT ON UPDATE CASCADE
);
Query OK, 0 rows affected (0.04 sec)

What just happened?

ON DELETE RESTRICT prevents deleting a customer who has orders. ON UPDATE CASCADE automatically updates order records if a customer_id changes. The ENUM limits status to valid values only.

Referential Actions

When you delete or update a parent record, what happens to child records? You decide:

RECOMMENDED

RESTRICT/CASCADE

RESTRICT prevents parent deletion if children exist. CASCADE updates children when parent changes. Safe and predictable.

SET NULL

Sets foreign key to NULL when parent is deleted. Use only when the relationship is truly optional.

Business Rule Constraints

Your database should understand your business. If BookVault never sells books for free, encode that rule. If orders require at least one item, make the database enforce it.

Business rules prevent logical inconsistencies. Data that's technically valid but business-wise nonsensical.

-- BookVault order_items with business rule constraints
-- Prevents zero quantities and ensures positive pricing
CREATE TABLE order_items (
    order_item_id INT AUTO_INCREMENT PRIMARY KEY,
    order_id INT NOT NULL,
    book_id INT NOT NULL,
    quantity INT NOT NULL CHECK (quantity > 0 AND quantity <= 50),
    unit_price DECIMAL(10,2) NOT NULL CHECK (unit_price > 0),
    FOREIGN KEY (order_id) REFERENCES orders(order_id)
        ON DELETE CASCADE,
    FOREIGN KEY (book_id) REFERENCES books(book_id)
        ON DELETE RESTRICT,
    UNIQUE KEY unique_book_per_order (order_id, book_id)
);
Query OK, 0 rows affected (0.05 sec)

What just happened?

The quantity check prevents ordering 0 books or unrealistic quantities (max 50). Unit price must be positive. The unique key prevents duplicate book entries in the same order — customers must update quantity instead of adding twice.

Complex Business Rules

Some rules span multiple columns or tables. BookVault might require that order total_amount matches the sum of all order_items. You can encode this with triggers or application-level validation.

-- BookVault categories table with hierarchical constraint
-- Prevents circular parent-child relationships
CREATE TABLE categories (
    category_id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL UNIQUE,
    parent_category_id INT NULL,
    FOREIGN KEY (parent_category_id) REFERENCES categories(category_id)
        ON DELETE SET NULL,
    CHECK (category_id != parent_category_id)
);
Query OK, 0 rows affected (0.03 sec)

Complete Schema Implementation

Here's the complete BookVault logical schema. Every table includes proper data types, constraints, and relationships. This schema prevents most common data problems automatically.

-- Complete BookVault logical schema
-- Production-ready with comprehensive constraints

-- Authors must exist before books reference them
CREATE TABLE authors (
    author_id INT AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL,
    nationality VARCHAR(50),
    birth_year INT CHECK (birth_year BETWEEN 1000 AND YEAR(CURDATE()))
);

-- Categories support hierarchical structure
CREATE TABLE categories (
    category_id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL UNIQUE,
    parent_category_id INT NULL,
    FOREIGN KEY (parent_category_id) REFERENCES categories(category_id)
        ON DELETE SET NULL,
    CHECK (category_id != parent_category_id)
);

-- Test the schema with sample data
INSERT INTO authors (first_name, last_name, nationality) 
VALUES ('Agatha', 'Christie', 'British');

INSERT INTO categories (name) VALUES ('Fiction');

SELECT author_id, CONCAT(first_name, ' ', last_name) as author_name 
FROM authors;
Query OK, 0 rows affected (0.01 sec)
Query OK, 0 rows affected (0.02 sec)
Query OK, 1 row affected (0.01 sec)
Query OK, 1 row affected (0.01 sec)

+-----------+----------------+
| author_id | author_name    |
+-----------+----------------+
|         1 | Agatha Christie|
+-----------+----------------+
1 row in set (0.00 sec)

The schema works! Authors and categories are ready. Now books can reference them safely. Each constraint prevents specific types of corruption, making the database self-protecting.

This logical schema becomes the blueprint for physical implementation. DBAs use it to create the actual database with proper indexing, storage optimization, and performance tuning.

Schema Design Reality

Perfect schemas don't exist. You'll discover missing constraints, need new data types, and adjust relationships as business requirements evolve. Design for change, document decisions, and version your schema like code.

Quiz

1. BookVault needs to store book prices. Which data type choice best prevents common pricing errors?


2. In the BookVault orders table, what should happen when trying to delete a customer who has existing orders?


3. BookVault wants to prevent customers from adding the same book twice to one order (they should update quantity instead). Which constraint accomplishes this?


Up Next

Physical Database Design

Transform your logical schema into an optimized physical database with indexing, partitioning, and performance considerations.