← Back to Lessons
BEGINNER ⏱ 35 minutes

Altering and Dropping Tables: Modifying Database Structure

Databases evolve over timeβ€”requirements change, new features are added, and old structures need updates. ALTER TABLE and DROP TABLE are the DDL commands that let you modify and remove database objects after creation. In this lesson, you'll learn how to add, modify, and remove columns; add and drop constraints; rename tables; and safely delete tables when they're no longer needed. Mastering these commands is essential for maintaining and evolving production databases.


πŸ“š What You'll Learn

1. ALTER TABLE: Modifying Existing Tables

ALTER TABLE lets you modify the structure of an existing table without recreating it or losing data. You can add columns, change data types, add constraints, and moreβ€”all while the table remains operational.

Common ALTER TABLE Operations

Operation Command Use Case
Add Column ADD COLUMN Add new fields to existing table
Modify Column MODIFY COLUMN Change data type or constraints
Drop Column DROP COLUMN Remove unwanted columns
Rename Column RENAME COLUMN Fix naming issues
Add Constraint ADD CONSTRAINT Add validation rules
Drop Constraint DROP CONSTRAINT Remove restrictions

2. Adding Columns to Existing Tables

Basic Syntax

ALTER TABLE table_name
ADD COLUMN column_name datatype constraints;

Examples: Adding Columns

-- Add a simple column
ALTER TABLE customers
ADD COLUMN phone VARCHAR(20);

-- Add column with NOT NULL and default
ALTER TABLE products
ADD COLUMN is_featured BOOLEAN NOT NULL DEFAULT FALSE;

-- Add column with position (after specific column)
ALTER TABLE employees
ADD COLUMN middle_name VARCHAR(100) AFTER first_name;

-- Add multiple columns at once
ALTER TABLE orders
ADD COLUMN shipping_address TEXT,
ADD COLUMN billing_address TEXT,
ADD COLUMN notes TEXT;

⚠️ Important: When adding a NOT NULL column to a table with existing data, you must provide a DEFAULT value. Otherwise, the operation fails because existing rows can't have NULL in the new column.

Adding Columns with Existing Data

-- ❌ This fails if table has data
ALTER TABLE customers
ADD COLUMN loyalty_points INT NOT NULL;
-- ERROR: Cannot add NOT NULL column without default

-- βœ… This works - provides default for existing rows
ALTER TABLE customers
ADD COLUMN loyalty_points INT NOT NULL DEFAULT 0;

-- βœ… Alternative: Make it nullable
ALTER TABLE customers
ADD COLUMN loyalty_points INT;

3. Modifying Existing Columns

Basic Syntax

ALTER TABLE table_name
MODIFY COLUMN column_name new_datatype new_constraints;

Examples: Modifying Columns

-- Change data type (make column larger)
ALTER TABLE products
MODIFY COLUMN description TEXT;  -- was VARCHAR(255)

-- Add NOT NULL constraint
ALTER TABLE customers
MODIFY COLUMN email VARCHAR(255) NOT NULL;

-- Remove NOT NULL constraint (make nullable)
ALTER TABLE employees
MODIFY COLUMN middle_name VARCHAR(100) NULL;

-- Change data type and add constraint
ALTER TABLE orders
MODIFY COLUMN status VARCHAR(20) NOT NULL DEFAULT 'pending';

⚠️ Warning: Changing a column's data type can cause data loss! For example, changing VARCHAR(255) to VARCHAR(50) truncates longer values. Changing INT to VARCHAR converts numbers to strings. Always verify compatibility before modifying.

Safe vs. Risky Modifications

βœ… Safe Modifications
Increasing column size (VARCHAR(50) β†’ VARCHAR(255))
Making a column nullable (NOT NULL β†’ NULL)
Adding a default value
Converting compatible types (INT β†’ BIGINT)
⚠️ Risky Modifications
Decreasing column size (VARCHAR(255) β†’ VARCHAR(50))
Making a column NOT NULL (may fail if NULLs exist)
Converting incompatible types (VARCHAR β†’ INT)
Changing precision (DECIMAL(10,2) β†’ DECIMAL(8,2))

4. Removing Columns

Basic Syntax

ALTER TABLE table_name
DROP COLUMN column_name;

Examples: Dropping Columns

-- Drop a single column
ALTER TABLE customers
DROP COLUMN fax_number;

-- Drop multiple columns
ALTER TABLE products
DROP COLUMN old_price,
DROP COLUMN legacy_id;

⚠️ Critical Warning: Dropping a column is permanent and irreversible. All data in that column is deleted immediately. Always backup your data before dropping columns!

What You Cannot Drop

-- ❌ This fails - column is a foreign key reference
ALTER TABLE orders
DROP COLUMN customer_id;
-- ERROR: Cannot drop column referenced by foreign key

-- βœ… Solution: Drop foreign key first, then column
ALTER TABLE orders
DROP FOREIGN KEY fk_order_customer;

ALTER TABLE orders
DROP COLUMN customer_id;

5. Renaming Columns and Tables

Rename Column

-- Rename a column
ALTER TABLE customers
RENAME COLUMN old_name TO new_name;

-- Example: Fix a typo
ALTER TABLE products
RENAME COLUMN prodcut_name TO product_name;

-- Example: Better naming
ALTER TABLE employees
RENAME COLUMN emp_id TO employee_id;

Rename Table

-- Rename a table
RENAME TABLE old_table_name TO new_table_name;

-- Example
RENAME TABLE customer TO customers;

-- Rename multiple tables at once
RENAME TABLE 
    old_customers TO customers,
    old_orders TO orders;

πŸ’‘ Note: Renaming doesn't affect data or indexes. Foreign keys and other constraints continue to work. However, any application code or queries using the old name will break, so update your application accordingly!

6. Managing Constraints

Adding Constraints

-- Add PRIMARY KEY
ALTER TABLE students
ADD PRIMARY KEY (student_id);

-- Add UNIQUE constraint
ALTER TABLE users
ADD CONSTRAINT uq_email UNIQUE (email);

-- Add FOREIGN KEY
ALTER TABLE orders
ADD CONSTRAINT fk_order_customer
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id);

-- Add CHECK constraint
ALTER TABLE products
ADD CONSTRAINT chk_positive_price CHECK (price > 0);

-- Add multiple constraints
ALTER TABLE employees
ADD CONSTRAINT uq_employee_email UNIQUE (email),
ADD CONSTRAINT chk_valid_salary CHECK (salary >= 0);

Dropping Constraints

-- Drop PRIMARY KEY
ALTER TABLE students
DROP PRIMARY KEY;

-- Drop UNIQUE constraint
ALTER TABLE users
DROP INDEX uq_email;

-- Drop FOREIGN KEY
ALTER TABLE orders
DROP FOREIGN KEY fk_order_customer;

-- Drop CHECK constraint
ALTER TABLE products
DROP CHECK chk_positive_price;

⚠️ Important: Adding constraints to tables with existing data can fail if the data violates the constraint. For example, adding UNIQUE fails if duplicates exist. Always check your data first!

7. Dropping Tables

Basic Syntax

DROP TABLE table_name;

Examples

-- Drop a single table
DROP TABLE temp_data;

-- Drop table if it exists (no error if missing)
DROP TABLE IF EXISTS old_table;

-- Drop multiple tables
DROP TABLE table1, table2, table3;

⚠️ EXTREME CAUTION: DROP TABLE is permanent and irreversible. The table and ALL its data are deleted immediately. There is no undo. Always backup before dropping production tables!

What Happens When You Drop a Table

8. DROP vs. TRUNCATE vs. DELETE

Feature DROP TABLE TRUNCATE TABLE DELETE FROM
What it does Deletes table + data Deletes all data, keeps table Deletes specific rows
Table structure ❌ Deleted βœ“ Preserved βœ“ Preserved
Data ❌ All deleted ❌ All deleted Selected rows deleted
WHERE clause ❌ No ❌ No βœ“ Yes
Speed Fast Very fast Slower (row by row)
Rollback ❌ No ❌ No (usually) βœ“ Yes (in transaction)
Resets AUTO_INCREMENT βœ“ Yes βœ“ Yes ❌ No
Use case Remove entire table Empty table, keep structure Remove specific rows

Examples

-- DROP TABLE - Remove table completely
DROP TABLE old_logs;

-- TRUNCATE TABLE - Delete all data, keep structure
TRUNCATE TABLE session_logs;

-- DELETE FROM - Remove specific rows
DELETE FROM users WHERE last_login < '2020-01-01';

9. Best Practices for Altering Tables

βœ… DO:

  • Always backup your database before ALTER operations
  • Test ALTER statements on a development copy first
  • Use transactions when possible
  • Schedule schema changes during low-traffic periods
  • Check data compatibility before modifying data types
  • Use IF EXISTS / IF NOT EXISTS to avoid errors
  • Document all schema changes
  • Communicate changes to your team
  • Keep old data for a grace period before dropping columns

❌ DON'T:

  • Run ALTER/DROP on production without testing
  • Drop columns without verifying they're unused
  • Decrease column sizes without checking data
  • Add NOT NULL without a DEFAULT on tables with data
  • Drop tables that have foreign key references
  • Make multiple schema changes simultaneously
  • Ignore error messages (they prevent data loss!)
  • Forget to update application code after schema changes

10. 🎯 Practice Exercises

Exercise 1: Modify an Existing Products Table

You have a products table and need to make the following changes:

  • Add a new column: weight (DECIMAL(8,2), nullable)
  • Add a new column: is_discontinued (BOOLEAN, default FALSE, not null)
  • Modify the description column from VARCHAR(255) to TEXT
  • Add a CHECK constraint: price must be greater than 0
Show Solution
-- Step 1: Add weight column (nullable, so no default needed)
ALTER TABLE products
ADD COLUMN weight DECIMAL(8, 2);

-- Step 2: Add is_discontinued (NOT NULL requires DEFAULT)
ALTER TABLE products
ADD COLUMN is_discontinued BOOLEAN NOT NULL DEFAULT FALSE;

-- Step 3: Modify description to larger type
ALTER TABLE products
MODIFY COLUMN description TEXT;

-- Step 4: Add CHECK constraint for price
ALTER TABLE products
ADD CONSTRAINT chk_positive_price CHECK (price > 0);

-- Verify changes
DESCRIBE products;

Key Points:

  • βœ… Nullable columns don't need defaults
  • βœ… NOT NULL columns need DEFAULT when table has data
  • βœ… Increasing text size (VARCHAR β†’ TEXT) is safe
  • βœ… Named constraints are easier to drop later

Exercise 2: Fix Column Naming Issues

A previous developer created a table with poor naming. Fix these issues:

  • Table name: customer should be customers
  • Column: cust_nm should be customer_name
  • Column: e_mail should be email
  • Column: ph should be phone
Show Solution
-- Step 1: Rename table
RENAME TABLE customer TO customers;

-- Step 2: Rename columns
ALTER TABLE customers
RENAME COLUMN cust_nm TO customer_name;

ALTER TABLE customers
RENAME COLUMN e_mail TO email;

ALTER TABLE customers
RENAME COLUMN ph TO phone;

-- Alternative: Rename multiple columns in one statement (MySQL 8.0+)
ALTER TABLE customers
RENAME COLUMN cust_nm TO customer_name,
RENAME COLUMN e_mail TO email,
RENAME COLUMN ph TO phone;

-- Verify changes
DESCRIBE customers;

Best Practices Applied:

  • βœ… Use descriptive, full names (not abbreviations)
  • βœ… Use plural for table names (customers, not customer)
  • βœ… Use underscores for multi-word names
  • βœ… Be consistent across your schema
  • ⚠️ Remember to update application code after renaming!

Exercise 3: Add Constraints to Existing Table

You have an employees table that was created without proper constraints. Add the following:

  • Make email UNIQUE
  • Add UNIQUE constraint on employee_number
  • Add CHECK: salary must be between 30000 and 500000
  • Add CHECK: hire_date cannot be in the future
  • Add foreign key: department_id references departments(department_id)
Show Solution
-- Step 1: Add UNIQUE constraints
ALTER TABLE employees
ADD CONSTRAINT uq_employee_email UNIQUE (email);

ALTER TABLE employees
ADD CONSTRAINT uq_employee_number UNIQUE (employee_number);

-- Step 2: Add CHECK constraint for salary
ALTER TABLE employees
ADD CONSTRAINT chk_salary_range 
    CHECK (salary BETWEEN 30000 AND 500000);

-- Step 3: Add CHECK constraint for hire_date
ALTER TABLE employees
ADD CONSTRAINT chk_hire_date_not_future 
    CHECK (hire_date <= CURRENT_DATE);

-- Step 4: Add foreign key
ALTER TABLE employees
ADD CONSTRAINT fk_employee_department
    FOREIGN KEY (department_id) REFERENCES departments(department_id);

-- Verify constraints
SHOW CREATE TABLE employees;

Important Notes:

  • βœ… All constraints have descriptive names
  • ⚠️ UNIQUE constraints will fail if duplicates exist
  • ⚠️ CHECK constraints will fail if data violates them
  • ⚠️ Foreign key will fail if invalid department_ids exist
  • πŸ’‘ Clean your data first before adding constraints!

How to Check for Issues First:

-- Check for duplicate emails
SELECT email, COUNT(*) 
FROM employees 
GROUP BY email 
HAVING COUNT(*) > 1;

-- Check for invalid salaries
SELECT * FROM employees 
WHERE salary < 30000 OR salary > 500000;

-- Check for invalid department_ids
SELECT * FROM employees 
WHERE department_id NOT IN (SELECT department_id FROM departments);

πŸ”₯ Challenge: Database Evolution Scenario

Your company is evolving its user system. Perform this multi-step migration:

Starting point: users table with columns: user_id, username, password, created_at

Requirements:

  • Split name: Add first_name and last_name columns
  • Add email (required, unique)
  • Add email_verified (BOOLEAN, default FALSE)
  • Add last_login (TIMESTAMP, nullable)
  • Rename password to password_hash
  • Add account_status (ENUM: active, suspended, deleted, default: active)
  • Add CHECK: username must be at least 3 characters
  • Drop the old username column (after data migration)
Show Solution
-- STEP 1: Add new columns
ALTER TABLE users
ADD COLUMN first_name VARCHAR(100) NOT NULL DEFAULT 'Unknown',
ADD COLUMN last_name VARCHAR(100) NOT NULL DEFAULT 'Unknown',
ADD COLUMN email VARCHAR(255) NOT NULL DEFAULT 'pending@example.com',
ADD COLUMN email_verified BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN last_login TIMESTAMP NULL,
ADD COLUMN account_status ENUM('active', 'suspended', 'deleted') 
    NOT NULL DEFAULT 'active';

-- STEP 2: Make email unique (after populating with real data)
-- First, you'd populate real emails in your application
-- Then add the unique constraint:
ALTER TABLE users
ADD CONSTRAINT uq_user_email UNIQUE (email);

-- STEP 3: Rename password column
ALTER TABLE users
RENAME COLUMN password TO password_hash;

-- STEP 4: Add CHECK constraint for username
ALTER TABLE users
ADD CONSTRAINT chk_username_length CHECK (LENGTH(username) >= 3);

-- STEP 5: In production, you'd migrate username data to first_name/last_name
-- For example:
-- UPDATE users SET 
--   first_name = SUBSTRING_INDEX(username, ' ', 1),
--   last_name = SUBSTRING_INDEX(username, ' ', -1);

-- STEP 6: After verifying data migration, drop username
-- (In production, wait several weeks to ensure no issues)
ALTER TABLE users
DROP COLUMN username;

-- Final structure verification
DESCRIBE users;
SHOW CREATE TABLE users;

Production Migration Strategy:

  • βœ… Add new columns with defaults first
  • βœ… Migrate data gradually (in batches)
  • βœ… Keep old columns for grace period (1-4 weeks)
  • βœ… Monitor for errors after each step
  • βœ… Update application code to use new columns
  • βœ… Only drop old columns after confirming stability

Real-World Timeline:

Week 1: Add new columns with defaults
Week 2: Deploy app code using BOTH old and new columns
Week 3: Migrate data from username to first_name/last_name
Week 4: Switch app to use only new columns
Week 5-6: Monitor for issues
Week 7: Drop old username column

πŸ’‘ Pro Tip: Never rush schema changes in production. Each step should be tested, deployed, and monitored independently. It's better to take 6 weeks safely than to cause a production outage!

πŸ“ Key Takeaways