MySQL Alter Table - Add Column
Requirements change: the customers table that once needed only a name and a city now needs an email, a phone number and a sign-up date. ALTER TABLE … ADD COLUMN adds new columns to an existing table without losing any data.
1. Syntax
ALTER TABLE table_name
ADD [COLUMN] column_name data_type [constraints] [FIRST | AFTER existing_column];
- The word
COLUMNis optional. - Without
FIRST/AFTER, the column goes at the end. - Existing rows receive the column's
DEFAULT, or NULL if there is none.
ADD COLUMN … AFTER places the new column exactly where you want it.2. Adding a Column
ALTER TABLE customers ADD COLUMN email VARCHAR(100);
SELECT * FROM customers;
Result: 3 rows
| id | name | city | |
|---|---|---|---|
| 1 | Ishaan | Pune | NULL |
| 2 | Fatima | Delhi | NULL |
| 3 | George | Chennai | NULL |
3. Choosing the Position: FIRST and AFTER
ALTER TABLE customers ADD COLUMN phone VARCHAR(15) AFTER name;
ALTER TABLE customers ADD COLUMN customer_code CHAR(6) FIRST;
DESCRIBE customers;
Result: 6 rows
| Field | Type | Null | Default |
|---|---|---|---|
| customer_code | char(6) | YES | NULL |
| id | int | NO | NULL |
| name | varchar(40) | NO | NULL |
| phone | varchar(15) | YES | NULL |
| city | varchar(30) | YES | NULL |
| varchar(100) | YES | NULL |
SELECT * and DESCRIBE. Queries that name their columns don't care about position.4. Adding a NOT NULL Column
A NOT NULL column needs a value for the rows that already exist. If you give no default, MySQL uses the type's implicit default (0, empty string, or a zero date), which is rarely what you want:
ALTER TABLE customers ADD COLUMN loyalty_points INT NOT NULL;
SELECT name, loyalty_points FROM customers;
Result: 3 rows
| name | loyalty_points |
|---|---|
| Ishaan | 0 |
| Fatima | 0 |
| George | 0 |
Give an explicit default so the meaning is clear:
ALTER TABLE customers
ADD COLUMN status VARCHAR(10) NOT NULL DEFAULT 'active',
ADD COLUMN joined_on DATE NOT NULL DEFAULT (CURRENT_DATE);
SELECT name, status, joined_on IS NOT NULL AS has_join_date FROM customers;
Result: 3 rows
| name | status | has_join_date |
|---|---|---|
| Ishaan | active | 1 |
| Fatima | active | 1 |
| George | active | 1 |
5. Adding Several Columns at Once
Separate several ADD clauses with commas. MySQL changes the table once, which is faster than separate statements:
ALTER TABLE customers
ADD COLUMN birth_date DATE AFTER phone,
ADD COLUMN newsletter BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN notes TEXT;
SELECT column_name, column_type
FROM information_schema.columns
WHERE table_schema = 'crm' AND table_name = 'customers'
ORDER BY ordinal_position;
Result: 12 rows
| COLUMN_NAME | COLUMN_TYPE |
|---|---|
| customer_code | char(6) |
| id | int |
| name | varchar(40) |
| phone | varchar(15) |
| birth_date | date |
| city | varchar(30) |
| varchar(100) | |
| loyalty_points | int |
| status | varchar(10) |
| joined_on | date |
| newsletter | tinyint(1) |
| notes | text |
6. Adding Columns with Constraints and Indexes
ALTER TABLE customers
ADD COLUMN pan_number CHAR(10) UNIQUE,
ADD COLUMN credit_limit DECIMAL(10,2) DEFAULT 10000 CHECK (credit_limit >= 0),
ADD INDEX idx_city (city);
UPDATE customers SET pan_number = 'ABCDE1234F' WHERE id = 1;
UPDATE customers SET pan_number = 'ABCDE1234F' WHERE id = 2;
Query OK, 3 rows affected
Records: 3 Duplicates: 0 Warnings: 0
Query OK, 1 row affected
Rows matched: 1 Changed: 1 Warnings: 0
ERROR 1062 (23000): Duplicate entry 'ABCDE1234F' for key 'customers.pan_number'
A foreign-key column works the same way; add the column, then the constraint:
CREATE TABLE regions (region_id INT PRIMARY KEY, region_name VARCHAR(20));
INSERT INTO regions VALUES (1, 'West'), (2, 'North'), (3, 'South');
ALTER TABLE customers
ADD COLUMN region_id INT,
ADD CONSTRAINT fk_customer_region FOREIGN KEY (region_id) REFERENCES regions(region_id);
UPDATE customers SET region_id = CASE city WHEN 'Pune' THEN 1 WHEN 'Delhi' THEN 2 ELSE 3 END;
SELECT c.name, r.region_name FROM customers c JOIN regions r ON r.region_id = c.region_id;
Result: 3 rows
| name | region_name |
|---|---|
| Ishaan | West |
| Fatima | North |
| George | South |
7. Adding a Generated Column
A generated column is calculated from other columns. Use VIRTUAL (computed on read) or STORED (saved on write):
ALTER TABLE customers
ADD COLUMN email_domain VARCHAR(60)
AS (SUBSTRING_INDEX(email, '@', -1)) VIRTUAL;
UPDATE customers SET email = CONCAT(LOWER(name), '@mail.example') WHERE id IN (1, 3);
SELECT name, email, email_domain FROM customers;
Result: 3 rows
| name | email_domain | |
|---|---|---|
| Ishaan | ishaan@mail.example | mail.example |
| Fatima | NULL | NULL |
| George | george@mail.example | mail.example |
8. Performance: INSTANT, INPLACE and COPY
On big tables, how MySQL adds the column matters. Since MySQL 8.0.29, most ADD COLUMN operations (at any position) use the INSTANT algorithm: only metadata changes, so even a table with millions of rows is altered in milliseconds. You can ask for it explicitly; MySQL raises an error rather than silently doing a slow copy if it can't:
ALTER TABLE customers ADD COLUMN referral_code VARCHAR(12), ALGORITHM = INSTANT;
Query OK, 0 rows affected
Records: 0 Duplicates: 0 Warnings: 0
| Algorithm | How it works | Table usable during the change? |
|---|---|---|
| INSTANT | Metadata only | Yes, practically no wait |
| INPLACE | Rebuilds inside InnoDB | Mostly yes (reads and writes allowed) |
| COPY | Copies every row into a new table | Writes blocked; slow on big tables |
STORED generated column) can take a long time on large tables. Test on a copy, and run it at a quiet time.9. Adding a Column Only If It Doesn't Exist
MySQL has no ADD COLUMN IF NOT EXISTS. Adding a duplicate fails:
ALTER TABLE customers ADD COLUMN email VARCHAR(100);
ERROR 1060 (42S21): Duplicate column name 'email'
Check information_schema.columns first, from a script or migration tool:
SELECT COUNT(*) AS email_exists
FROM information_schema.columns
WHERE table_schema = 'crm' AND table_name = 'customers' AND column_name = 'email';
Result: 1 row
| email_exists |
|---|
| 1 |
ALTER TABLE runs exactly once.10. Practice Exercises
Exercise 1: Add a gst_number column (15 characters) right after pan_number.
ALTER TABLE customers ADD COLUMN gst_number CHAR(15) AFTER pan_number;
SHOW COLUMNS FROM customers LIKE '%number';
Result: 2 rows
| Field | Type |
|---|---|
| pan_number | char(10) |
| gst_number | char(15) |
Exercise 2: Add a required tier column that only accepts 'silver', 'gold' or 'platinum' and defaults to 'silver'.
ALTER TABLE customers ADD COLUMN tier ENUM('silver','gold','platinum') NOT NULL DEFAULT 'silver';
SELECT name, tier FROM customers;
Result: 3 rows
| name | tier |
|---|---|
| Ishaan | silver |
| Fatima | silver |
| George | silver |
11. Summary
ALTER TABLE t ADD COLUMN c type;adds a column; existing rows get the default or NULL.- Use
FIRSTorAFTER colto choose the position. - Give
NOT NULLcolumns an explicitDEFAULT. - Add several columns, constraints and indexes in one
ALTER TABLE. - Most column additions are INSTANT in MySQL 8.0.29+; there is no IF NOT EXISTS, so check information_schema.