Skip to content

Basic Schema Example

Learn database design fundamentals with a simple user-posts schema.

Overview

This example demonstrates:

  • Creating basic tables
  • Defining columns with constraints
  • Setting up primary keys
  • Creating foreign key relationships
  • Adding sample data

Schema Design

Visual Representation

┌──────────────────┐         ┌──────────────────┐
│     users        │         │     posts        │
├──────────────────┤         ├──────────────────┤
│ ● id (PK)        │────────<│ ● id (PK)        │
│ ○ username       │   1:N   │ ● user_id (FK)   │
│ ○ email          │         │ ○ title          │
│ ○ created_at     │         │ ○ content        │
└──────────────────┘         │ ○ published_at   │
                             └──────────────────┘

Relationship

  • One user can have many posts
  • One post belongs to one user
  • Cardinality: 1:N (One-to-Many)

Step-by-Step Implementation

Step 1: Create Users Table

  1. Click "+ Add Table"
  2. Fill in the form:
Table Name: users

Columns:
┌─────────────┬───────────┬────────┬──────────────────────┐
│ Name        │ Type      │ Length │ Constraints          │
├─────────────┼───────────┼────────┼──────────────────────┤
│ id          │ INT       │        │ ☑ PK  ☑ AI          │
│ username    │ VARCHAR   │ 50     │ ☑ NOT NULL          │
│ email       │ VARCHAR   │ 100    │ ☑ NOT NULL          │
│ created_at  │ TIMESTAMP │        │                      │
└─────────────┴───────────┴────────┴──────────────────────┘
  1. Click "Create Table"

Generated SQL:

sql
CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(50) NOT NULL,
  email VARCHAR(100) NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Step 2: Create Posts Table

  1. Press Cmd+N (or click Add Table)
  2. Configure the table:
Table Name: posts

Columns:
┌──────────────┬───────────┬────────┬──────────────────────┐
│ Name         │ Type      │ Length │ Constraints          │
├──────────────┼───────────┼────────┼──────────────────────┤
│ id           │ INT       │        │ ☑ PK  ☑ AI          │
│ user_id      │ INT       │        │ ☑ NOT NULL          │
│ title        │ VARCHAR   │ 200    │ ☑ NOT NULL          │
│ content      │ TEXT      │        │                      │
│ published_at │ TIMESTAMP │        │                      │
└──────────────┴───────────┴────────┴──────────────────────┘
  1. Click "Create Table"

Generated SQL:

sql
CREATE TABLE posts (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL,
  title VARCHAR(200) NOT NULL,
  content TEXT,
  published_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Step 3: Create Relationship

Visual Method:

  1. Hover over posts.user_id column
  2. Click the right handle (●)
  3. Drag to users.id left handle
  4. Release to create FK

SQL Generated:

sql
ALTER TABLE posts
ADD CONSTRAINT fk_posts_user_id
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE
ON UPDATE CASCADE;

Step 4: Add Sample Data

Insert Users

  1. Click on users table
  2. Click "Browse Data"
  3. Click "Add Row" and enter:
sql
-- User 1
INSERT INTO users (username, email) VALUES 
('johndoe', 'john@example.com');

-- User 2
INSERT INTO users (username, email) VALUES 
('janedoe', 'jane@example.com');

-- User 3
INSERT INTO users (username, email) VALUES 
('bobsmith', 'bob@example.com');

Insert Posts

  1. Click on posts table
  2. Click "Browse Data"
  3. Add posts:
sql
-- Posts by John (user_id = 1)
INSERT INTO posts (user_id, title, content) VALUES 
(1, 'My First Post', 'Hello World! This is my first blog post.');

INSERT INTO posts (user_id, title, content) VALUES 
(1, 'Learning SQL', 'Today I learned about foreign keys and relationships.');

-- Posts by Jane (user_id = 2)
INSERT INTO posts (user_id, title, content) VALUES 
(2, 'Introduction to Databases', 'Databases are essential for modern applications.');

-- Posts by Bob (user_id = 3)
INSERT INTO posts (user_id, title, content) VALUES 
(3, 'Why I Love MySQL', 'MySQL is fast, reliable, and easy to use.');

Step 5: Query the Data

View all posts with usernames:

sql
SELECT 
  posts.id,
  posts.title,
  posts.content,
  users.username,
  posts.published_at
FROM posts
JOIN users ON posts.user_id = users.id
ORDER BY posts.published_at DESC;

Result:

┌────┬──────────────────────────┬─────────────┬─────────────────────┐
│ id │ title                    │ username    │ published_at        │
├────┼──────────────────────────┼─────────────┼─────────────────────┤
│ 4  │ Why I Love MySQL         │ bobsmith    │ 2026-06-26 14:27:00 │
│ 3  │ Introduction to Databases│ janedoe     │ 2026-06-26 14:26:45 │
│ 2  │ Learning SQL             │ johndoe     │ 2026-06-26 14:26:30 │
│ 1  │ My First Post            │ johndoe     │ 2026-06-26 14:26:15 │
└────┴──────────────────────────┴─────────────┴─────────────────────┘

Key Concepts Explained

Primary Key (PK)

sql
id INT AUTO_INCREMENT PRIMARY KEY
  • Uniquely identifies each row
  • Auto-increments for new rows
  • Cannot be NULL
  • Must be unique

Foreign Key (FK)

sql
user_id INT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
  • Links to another table (users.id)
  • Enforces referential integrity
  • Prevents orphaned records
  • ON DELETE CASCADE: Delete posts when user deleted

NOT NULL Constraint

sql
username VARCHAR(50) NOT NULL
  • Field is required
  • Cannot insert without value
  • Ensures data integrity

TIMESTAMP

sql
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  • Automatically set on insert
  • Tracks when record was created
  • Useful for auditing

Testing the Relationship

  1. In posts data browser
  2. Click "Related" button on any post
  3. See the parent user record

Verify Referential Integrity

Try inserting invalid data:

sql
-- This will FAIL (user_id 999 doesn't exist)
INSERT INTO posts (user_id, title, content) VALUES 
(999, 'Invalid Post', 'This will not work');

-- Error: Cannot add or update a child row: 
-- a foreign key constraint fails

Test CASCADE Delete

sql
-- Delete a user
DELETE FROM users WHERE id = 1;

-- All posts by that user are also deleted (CASCADE)
SELECT * FROM posts WHERE user_id = 1;
-- Returns empty (no orphaned posts)

Common Queries

Get all posts by a user

sql
SELECT * FROM posts 
WHERE user_id = 1
ORDER BY published_at DESC;

Count posts per user

sql
SELECT 
  users.username,
  COUNT(posts.id) as post_count
FROM users
LEFT JOIN posts ON users.id = posts.user_id
GROUP BY users.id, users.username
ORDER BY post_count DESC;

Find users with no posts

sql
SELECT users.* 
FROM users
LEFT JOIN posts ON users.id = posts.user_id
WHERE posts.id IS NULL;

Recent posts with user info

sql
SELECT 
  posts.title,
  posts.content,
  users.username,
  users.email,
  posts.published_at
FROM posts
JOIN users ON posts.user_id = users.id
WHERE posts.published_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
ORDER BY posts.published_at DESC;

Best Practices

✅ DO

  • Use INT for primary keys
  • Always add PRIMARY KEY constraint
  • Use AUTO_INCREMENT for IDs
  • Add NOT NULL to required fields
  • Use meaningful column names (user_id, not uid)
  • Add timestamps for auditing

❌ DON'T

  • Don't use VARCHAR for IDs
  • Don't forget foreign key indexes
  • Don't allow NULL in required fields
  • Don't use vague names (data, value, info)
  • Don't skip referential integrity

Schema Evolution

Add Published Status

sql
ALTER TABLE posts
ADD COLUMN is_published BOOLEAN DEFAULT FALSE;

Add View Count

sql
ALTER TABLE posts
ADD COLUMN view_count INT DEFAULT 0;

Add User Profile

sql
ALTER TABLE users
ADD COLUMN bio TEXT,
ADD COLUMN avatar_url VARCHAR(255);

Extending the Schema

Add Comments Table

posts (1) ────< comments (N)
users (1) ────< comments (N)
sql
CREATE TABLE comments (
  id INT AUTO_INCREMENT 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(id) ON DELETE CASCADE,
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

Add Categories

categories (1) ────< posts (N)
sql
CREATE TABLE categories (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50) NOT NULL UNIQUE,
  slug VARCHAR(50) NOT NULL UNIQUE
);

ALTER TABLE posts
ADD COLUMN category_id INT,
ADD FOREIGN KEY (category_id) REFERENCES categories(id);

Practice Exercises

Exercise 1: Add Likes

Create a table to track post likes:

  • User can like multiple posts
  • Post can have multiple likes
  • No duplicate likes (user + post unique)
Solution
sql
CREATE TABLE post_likes (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL,
  post_id INT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
  FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
  UNIQUE KEY unique_like (user_id, post_id)
);

Exercise 2: Query Optimization

Add indexes for common queries:

  • Posts by user_id
  • Posts by published_at
Solution
sql
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_published_at ON posts(published_at);
CREATE INDEX idx_posts_user_published ON posts(user_id, published_at);

Next Steps

Learn more advanced patterns:

Resources

Released under the MIT License.