Chapter 7

CREATE & ALTER Tables

Learn how to design and build your own database tables from scratch. Master the art of schema design!

📋 Creating Tables with CREATE TABLE

So far, you've been querying existing tables. Now it's time to learn how to create your own tables from scratch!

🎯 Real-Life Analogy: CREATE TABLE is Like Designing a Form

Imagine creating a paper form for a survey:

  • Table Name: "Customer Survey" (what the form is called)
  • Columns: Questions on the form (Name, Email, Age, Feedback)
  • Data Types: Type of answer expected (text, number, date)
  • Constraints: Rules ("Name is required", "Email must be unique")

CREATE TABLE is like designing the blank form - you define what information goes where!

CREATE TABLE Syntax:

CREATE TABLE table_name (
    column1 data_type constraints,
    column2 data_type constraints,
    column3 data_type constraints,
    ...
);

Try It: Create a Simple Table

Output will appear here...

🔍 What Just Happened?

  1. CREATE TABLE students: We created a new table called "students"
  2. Defined columns: id, name, age, grade
  3. Set data types: INTEGER for numbers, TEXT for strings
  4. Added constraints: PRIMARY KEY, NOT NULL
  5. Tested it: Inserted data to make sure it works!

Real-World Example: Creating a Products Table

CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,
    product_name TEXT NOT NULL,
    price REAL,
    stock_quantity INTEGER DEFAULT 0,
    category TEXT,
    created_date TEXT
);

This creates a table to store product information for an e-commerce store!

🔤 SQL Data Types

Data types tell SQL what kind of information each column will store.

🎯 Real-Life Analogy: Data Types are Like Container Types

Think about storing different things:

  • INTEGER (number box): Can only hold whole numbers like 42, -17, 1000
  • TEXT (string box): Holds any text like "Hello", "abc123"
  • REAL (decimal box): Holds numbers with decimals like 19.99, 3.14
  • DATE (calendar box): Holds dates like "2024-01-15"

You wouldn't store apples in a shoe box - same idea with data types!

Common SQLite Data Types:

INTEGER

Whole numbers (no decimals)

age INTEGER

Examples: 42, -17, 1000

REAL

Numbers with decimals

price REAL

Examples: 19.99, 3.14159, -0.5

TEXT

Any text or string

name TEXT

Examples: "Alice", "hello@email.com"

BLOB

Binary data (images, files)

photo BLOB

Examples: Images, PDFs, binary files

NULL

Represents missing/unknown

email TEXT

Meaning: No value provided

Other Database Systems (MySQL, PostgreSQL):

Different databases have more specific types:

  • VARCHAR(50) - Text with max length of 50 characters
  • INT - Same as INTEGER
  • DECIMAL(10,2) - Numbers with fixed decimal places
  • DATE - Date only (YYYY-MM-DD)
  • DATETIME - Date and time
  • BOOLEAN - TRUE or FALSE

Try It: Create Table with Various Data Types

Output will appear here...

🔍 Choosing the Right Data Type:

  • Ages, IDs, counts: INTEGER
  • Prices, measurements: REAL or DECIMAL
  • Names, descriptions: TEXT or VARCHAR
  • Dates: TEXT (in SQLite) or DATE (in MySQL/PostgreSQL)
  • True/False flags: INTEGER (0/1) or BOOLEAN

⚡ Constraints - Rules for Your Data

Constraints enforce rules to keep your data clean and consistent!

🎯 Real-Life Analogy: Constraints are Like Form Validation

When filling out an online form:

  • NOT NULL: "This field is required" (can't be empty)
  • UNIQUE: "Email already in use" (must be unique)
  • DEFAULT: Country field pre-filled with "USA"
  • CHECK: "Age must be 18 or older" (validation rule)

Constraints prevent bad data from entering your database!

Common Constraints:

PRIMARY KEY

Unique identifier for each row

id INTEGER PRIMARY KEY

Automatically NOT NULL and UNIQUE

NOT NULL

Column must have a value

name TEXT NOT NULL

Prevents empty/missing data

UNIQUE

No duplicate values allowed

email TEXT UNIQUE

Ensures uniqueness (like usernames)

DEFAULT

Sets a default value

status TEXT DEFAULT 'active'

Uses default if nothing provided

CHECK

Custom validation rule

age INTEGER CHECK(age >= 18)

Enforces business logic

FOREIGN KEY

Links to another table

dept_id INTEGER REFERENCES departments(id)

Maintains relationships

Try It: Create Table with Multiple Constraints

Output will appear here...

Try It: See Constraints in Action (This Will Fail!)

Watch how constraints prevent bad data:

Output will appear here...

You should see an error - constraints are working to protect your data!

🔍 Why Use Constraints?

  • Data Integrity: Prevents invalid data from entering the database
  • Consistency: Ensures all data follows the same rules
  • Error Prevention: Catches mistakes before they become problems
  • Documentation: Constraints describe business rules in the database itself

🔑 Primary Keys Explained

Every table needs a unique identifier - that's the primary key!

🎯 Real-Life Analogy: Primary Keys are Like Student ID Numbers

In a school:

  • Student Name: "John Smith" - Multiple Johns might exist (not unique!)
  • Student ID: "STU-12345" - Unique to each student (perfect identifier!)

Even if two students have the same name, their ID numbers are always different!

Primary Key Rules:

  • Must be UNIQUE (no duplicates)
  • Cannot be NULL (must have a value)
  • Should never change (stable identifier)
  • Each table can have only ONE primary key
  • Often an INTEGER that auto-increments

Try It: Primary Key with AUTOINCREMENT

Output will appear here...

🔍 How AUTOINCREMENT Works:

  1. First row gets ID = 1
  2. Second row gets ID = 2
  3. Third row gets ID = 3
  4. Even if you delete row 2, the next new row gets ID = 4 (numbers never reused!)

You don't have to manually assign IDs - the database does it for you!

Common Primary Key Patterns:

-- Pattern 1: Simple integer ID
CREATE TABLE products (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    ...
);

-- Pattern 2: Composite primary key (multiple columns)
CREATE TABLE enrollments (
    student_id INTEGER,
    course_id INTEGER,
    PRIMARY KEY (student_id, course_id)
);

-- Pattern 3: UUID/GUID (advanced)
CREATE TABLE sessions (
    session_id TEXT PRIMARY KEY,  -- e.g., "a1b2c3d4-..."
    ...
);

🔧 ALTER TABLE - Modifying Existing Tables

Need to change a table structure after it's created? Use ALTER TABLE!

🎯 Real-Life Analogy: ALTER TABLE is Like Renovating a Building

Your house (table) is already built, but you want to:

  • Add a new room: ALTER TABLE ADD COLUMN
  • Remove a room: ALTER TABLE DROP COLUMN
  • Rename the house: ALTER TABLE RENAME TO
  • Remodel a room: ALTER TABLE MODIFY COLUMN

ALTER lets you modify without tearing down and rebuilding!

1. Adding New Columns:

Try It: Add Column to Existing Table

Output will appear here...

ALTER TABLE ADD COLUMN Syntax:

ALTER TABLE table_name
ADD COLUMN column_name data_type constraints;

Note: Existing rows will have NULL for the new column (unless you specify DEFAULT)

2. Renaming Tables:

Try It: Rename a Table

Output will appear here...

🔍 SQLite ALTER Limitations:

SQLite has limited ALTER capabilities compared to other databases:

  • ✅ Can: ADD COLUMN
  • ✅ Can: RENAME TABLE
  • ✅ Can: RENAME COLUMN (SQLite 3.25+)
  • ❌ Cannot: DROP COLUMN (need workaround)
  • ❌ Cannot: MODIFY COLUMN (need workaround)
  • ❌ Cannot: Add/Remove constraints on existing columns

MySQL and PostgreSQL support more ALTER operations!

Workaround: Recreating Table to Remove Column

-- Steps to remove a column in SQLite:

-- 1. Create new table without the unwanted column
CREATE TABLE employees_new (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL
    -- removed 'age' column
);

-- 2. Copy data from old table
INSERT INTO employees_new SELECT id, name FROM employees_old;

-- 3. Drop old table
DROP TABLE employees_old;

-- 4. Rename new table
ALTER TABLE employees_new RENAME TO employees_old;

🗑️ DROP TABLE - Deleting Tables

Need to completely remove a table? Use DROP TABLE (but be careful!)

⚠️ Warning: DROP TABLE is Permanent!

Think of DROP TABLE like demolishing a building:

  • The table structure is completely deleted
  • All data inside is permanently lost
  • There's NO UNDO button!

Always backup your data before using DROP TABLE!

DROP TABLE Syntax:

DROP TABLE table_name;

-- Safer version (won't error if table doesn't exist):
DROP TABLE IF EXISTS table_name;

Try It: Create and Drop a Test Table

Output will appear here...

Safe Dropping Pattern:

-- Check if table exists before dropping
DROP TABLE IF EXISTS old_data;

-- Create fresh table
CREATE TABLE new_data (
    id INTEGER PRIMARY KEY,
    info TEXT
);

-- This won't error even if old_data didn't exist

🏗️ Schema Design Basics

Learn to design good database structures - the foundation of great applications!

🎯 What is a Schema?

A database schema is the blueprint of your database - the overall structure of tables, columns, relationships, and constraints.

Think of it like architectural plans for a house:

  • Tables: Rooms in the house
  • Columns: Features of each room
  • Relationships: How rooms connect (hallways, doors)
  • Constraints: Building codes and rules

Good Schema Design Principles:

1. Normalization

Avoid data duplication

Store each piece of info once

2. Meaningful Names

Use clear table/column names

users, products, order_items

3. Primary Keys

Every table needs a unique ID

Use INTEGER AUTOINCREMENT

4. Right Data Types

Choose appropriate types

INTEGER for counts, REAL for prices

5. Constraints

Enforce data integrity

NOT NULL, UNIQUE, CHECK

6. Documentation

Add comments explaining tables

Make intent clear

Example: Well-Designed E-Commerce Schema

-- Customers table
CREATE TABLE customers (
    customer_id INTEGER PRIMARY KEY AUTOINCREMENT,
    email TEXT NOT NULL UNIQUE,
    first_name TEXT NOT NULL,
    last_name TEXT NOT NULL,
    created_at TEXT DEFAULT CURRENT_TIMESTAMP
);

-- Products table
CREATE TABLE products (
    product_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    description TEXT,
    price REAL NOT NULL CHECK(price >= 0),
    stock_quantity INTEGER DEFAULT 0
);

-- Orders table
CREATE TABLE orders (
    order_id INTEGER PRIMARY KEY AUTOINCREMENT,
    customer_id INTEGER NOT NULL,
    order_date TEXT DEFAULT CURRENT_TIMESTAMP,
    total_amount REAL,
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

-- Order Items (junction table for many-to-many)
CREATE TABLE order_items (
    order_id INTEGER,
    product_id INTEGER,
    quantity INTEGER NOT NULL CHECK(quantity > 0),
    price_at_purchase REAL NOT NULL,
    PRIMARY KEY (order_id, product_id),
    FOREIGN KEY (order_id) REFERENCES orders(order_id),
    FOREIGN KEY (product_id) REFERENCES products(product_id)
);

🔍 Why This Design is Good:

  • No duplication: Customer info stored once, not repeated in every order
  • Clear relationships: Foreign keys link tables properly
  • Data integrity: Constraints prevent invalid data (negative prices, empty names)
  • Scalable: Can add new products/customers without redesigning
  • Junction table: order_items handles many-to-many relationship

Practice Exercises

Part 1: Guided Exercises

Guided Exercise 1: Create a Library Table

Task: Create a table called "library_books" with columns for: id (primary key), title (required), author (required), isbn (unique), year_published (integer), available (default 1 for true).

Output will appear here...

Part 2: Independent Practice

Challenge 1: Employee Management System

Difficulty: Medium

Task: Create an "employees_new" table with: emp_id (PK, autoinc), full_name (required), email (required, unique), hire_date (text), salary (real, must be >= 0), is_active (default 1).

Output will appear here...

📝 Knowledge Check Quiz

1. Which constraint ensures a column cannot be empty?

2. What data type should you use for prices like $19.99?

3. What does AUTOINCREMENT do?