ALTER TABLE: Adding, Modifying and Removing Columns
ALTER changes a table that already holds data. Add and drop columns, change types, set defaults and manage constraints without losing rows.
- SQL Basics
- DDL
- DML
- SELECT
- WHERE
- Functions
- NULL and Logic
- Aggregate Functions
- GROUP BY
- JOIN
- Subqueries
- Set Operations
- CTEs
- Constraints
- Keys
- Relationships
- Database Design
- Normalisation
- Views
- Window Functions
- Advanced SQL
- Procedures and Functions
- Triggers
- Temporary Tables
- Transactions
- Isolation and Locking
- Indexes
- Query Performance
- SQL Security
- SQL Dialects
- Practical SQL
Concept
ALTER TABLE changes the definition of a table that already exists, usually one that already holds data. That is what makes it riskier than CREATE: every change has to be compatible with the rows already stored.
Syntax
ALTER TABLE table_name ADD COLUMN column_name data_type [constraint];
ALTER TABLE table_name DROP COLUMN column_name;
ALTER TABLE table_name ADD CONSTRAINT name constraint_definition;
ALTER TABLE table_name DROP CONSTRAINT name;
-- Changing a column type is the least portable operation in SQL
ALTER TABLE table_name MODIFY COLUMN column_name new_type; -- MySQL, Oracle
ALTER TABLE table_name ALTER COLUMN column_name TYPE new_type; -- PostgreSQL
ALTER TABLE table_name ALTER COLUMN column_name new_type; -- SQL ServerExample
-- 1. Add a nullable column: always safe
ALTER TABLE employees ADD COLUMN phone VARCHAR(20) NULL;
-- 2. Add a NOT NULL column: needs a default, or existing rows have no value
ALTER TABLE employees
ADD COLUMN country CHAR(2) NOT NULL DEFAULT 'IN';
-- 3. Widen a column: safe, no data can be lost
ALTER TABLE employees MODIFY COLUMN last_name VARCHAR(80) NOT NULL;
-- 4. Add a constraint after cleaning the data
UPDATE employees SET salary = 0 WHERE salary < 0;
ALTER TABLE employees
ADD CONSTRAINT ck_employees_salary CHECK (salary >= 0);
-- 5. Remove a column: destructive and usually irreversible
ALTER TABLE employees DROP COLUMN phone;Explanation
Step 2 is the one people get wrong. Adding a NOT NULL column to a table with existing rows must supply a value for those rows, otherwise the statement is contradictory and the database rejects it. A DEFAULT solves it. If no sensible default exists, do it in three steps: add the column as nullable, backfill it with UPDATE, then tighten it to NOT NULL.
Step 4 shows the general rule for constraints: the data must already satisfy the rule before the rule can be added.
Renaming
-- Modern MySQL 8, MariaDB 10.5+, PostgreSQL, Oracle
ALTER TABLE employees RENAME COLUMN phone TO contact_phone;
ALTER TABLE employees RENAME TO staff;
-- Older MySQL required the full column definition again
ALTER TABLE employees CHANGE phone contact_phone VARCHAR(20) NULL;
-- SQL Server uses a system stored procedure
EXEC sp_rename 'employees.phone', 'contact_phone', 'COLUMN';Important rules
- Widening a type is safe; narrowing it can truncate or fail.
VARCHAR(50)toVARCHAR(100)is fine, the reverse is not. - Dropping a column destroys its data. There is no undo without a backup.
- Adding a
NOT NULLcolumn to a populated table requires a default or a backfill. - On large tables
ALTERcan rewrite the whole table and hold locks. MySQL 8 does many changes online withALGORITHM=INPLACE, but not all of them. - You cannot drop a column another object depends on - an index, a view or a foreign key - until that object is dropped or changed.
Common mistakes
- Running an
ALTERon a multi million row table during peak hours and locking the application out. - Adding a
UNIQUEconstraint before removing duplicate values. - Renaming a column and forgetting the views, stored procedures and application queries that still use the old name.
- Assuming
MODIFY COLUMNkeeps the old attributes - in MySQL it replaces the definition, so a forgottenNOT NULLsilently becomes nullable.
Best practices
- Ship schema changes as small, reversible migrations, each with a written rollback.
- Test every migration against a copy of production sized data, not an empty table.
- Prefer add-then-backfill-then-tighten over one big statement.
- Deploy in the expand and contract order: add the new column, ship code that writes both, backfill, switch reads, then drop the old column in a later release.
Practice
- Add a
termination_date DATE NULLcolumn toemployees, then aCHECKthat it is never earlier thanhire_date. - Write the three step migration that turns the existing nullable
employees.emailinto aNOT NULL UNIQUEcolumn. - Why can adding a column with a default be instant in MySQL 8 but slow in an older version?