Change a table with ALTER TABLE
40 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)Tables change after they are built: the store wants loyalty points, a column name turns out to be unclear, a text column is too short. How do you change a table that already holds rows, without losing them?
ADD COLUMN with and without a default
ALTER TABLE changes the design of a table that already exists, such as adding, renaming, changing or dropping a column. Each kind of change does something specific to the rows already stored.
ALTER TABLE t ADD COLUMN name TYPE adds a new column at the end of the table.
Example 1 · ShopLite categories get two new columns
ALTER TABLE categories ADD COLUMN is_active BOOLEAN NOT NULL DEFAULT TRUE;ALTER TABLE categories ADD COLUMN description VARCHAR(200);
SELECT * FROM categories ORDER BY category_id;+-------------+-------------+-----------+-----------+-------------+ | category_id | name | parent_id | is_active | description | +-------------+-------------+-----------+-----------+-------------+ | 1 | Electronics | NULL | 1 | NULL | | 2 | Mobiles | 1 | 1 | NULL | | 3 | Laptops | 1 | 1 | NULL | | 4 | Accessories | 1 | 1 | NULL | | 5 | Books | NULL | 1 | NULL | | 6 | Fashion | NULL | 1 | NULL | | 7 | Toys | NULL | 1 | NULL | +-------------+-------------+-----------+-----------+-------------+
What this code does
- All 7 existing categories got
is_active = 1from the default, anddescription = NULLbecause that column has no default and allows NULL. - The rows were not touched otherwise. The store can now hide a category by setting
is_activeto 0 instead of deleting it.
Example 2 · NOT NULL without a default fills in empty values
CREATE TABLE ipl_players ( player_id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(40) NOT NULL, team CHAR(3) NOT NULL);
INSERT INTO ipl_players (name, team) VALUES ('Virat Kohli', 'RCB'), ('MS Dhoni', 'CSK'), ('Rohit Sharma', 'MI');
ALTER TABLE ipl_players ADD COLUMN matches INT NOT NULL;ALTER TABLE ipl_players ADD COLUMN country VARCHAR(30) NOT NULL;ALTER TABLE ipl_players ADD COLUMN role VARCHAR(20) NOT NULL DEFAULT 'Batter' AFTER name;
SELECT * FROM ipl_players ORDER BY player_id;+-----------+--------------+--------+------+---------+---------+ | player_id | name | role | team | matches | country | +-----------+--------------+--------+------+---------+---------+ | 1 | Virat Kohli | Batter | RCB | 0 | | | 2 | MS Dhoni | Batter | CSK | 0 | | | 3 | Rohit Sharma | Batter | MI | 0 | | +-----------+--------------+--------+------+---------+---------+
What this code does
matchesandcountryareNOT NULLwith no default, so MySQL filled the old rows with0and an empty text: thecountrycells look blank, but they are'', not NULL. Nothing warned about it.rolehas a real default, so every player gotBatter, andAFTER nameplaced the column second. Always give a default when adding a required column to a table with rows.
The rows already in the table must get some value in it, and which value depends on how you define the column.
- No rules (
ADD COLUMN gst_no VARCHAR(15)): every existing row getsNULL, meaning "no value yet". - With a default (
ADD COLUMN loyalty_points INT NOT NULL DEFAULT 0): every existing row gets the default. This is the clean way to add a required column. NOT NULLwith no default: MySQL fills existing rows with the type's built-in empty value:0for numbers, an empty text''forVARCHAR. The insert of a new row then fails if it leaves the column out. That silent''is a trap.
By default the column goes last. AFTER other_column places it after a given column, and FIRST makes it the first column. Order only affects how SELECT * and DESCRIBE list the columns.
NOT NULL means a value is required, and DEFAULT is the value used when an insert leaves the column out. The Keys & Constraints module covers both in depth.RENAME COLUMN and RENAME TABLE
ALTER TABLE t RENAME COLUMN old TO new changes only a column's name; its type, rules and data stay the same. It works in MySQL 8.0 and later.
Example 3 · Renaming a column and a table
CREATE TABLE tbl_marks ( roll INT, amt TINYINT);INSERT INTO tbl_marks (roll, amt) VALUES (1, 88), (2, 74);
ALTER TABLE tbl_marks RENAME COLUMN amt TO marks;ALTER TABLE tbl_marks RENAME COLUMN roll TO roll_no;RENAME TABLE tbl_marks TO exam_marks;
SELECT * FROM exam_marks ORDER BY roll_no;SELECT amt FROM exam_marks;+---------+-------+ | roll_no | marks | +---------+-------+ | 1 | 88 | | 2 | 74 | +---------+-------+ ERROR 1054 (42S22): Unknown column 'amt' in 'field list'
What this code does
- The data survived both renames: same rows, same values, new names.
- The old name
amtno longer exists, so the last query fails with error 1054. Every query and app using the old name breaks in the same way.
RENAME TABLE old TO new (or ALTER TABLE old RENAME TO new) renames a whole table, again keeping every row.
Why rename? Names are read far more often than they are written. amt becomes amount, tbl_students becomes students, and every later query is easier to read. The risk is outside the table: every query, report and app that uses the old name breaks at once, for example with ERROR 1054 (42S22): Unknown column 'name' in 'field list'. On a shared database, rename only together with the code that uses the name.
Fix the error
Fix the query
Read the real error, then pick the line that fixes it.
The column was renamed, but the query still fails.
Error
ERROR 1054 (42S22): Unknown column 'amt' in 'field list'
MODIFY and CHANGE COLUMN
MODIFY changes a column's type or rules and keeps its name. CHANGE renames and redefines it in one step. Both replace the whole column definition, so write everything you want to keep.
MODIFY column new_definition changes a column's type or rules and keeps its name: MODIFY name VARCHAR(80). CHANGE old_name new_name new_definition renames and redefines in one step: CHANGE city home_city VARCHAR(40). With CHANGE you must always write the full type, even if only the name changes.
Example 4 · MODIFY replaces the whole definition
CREATE TABLE hostel_residents ( roll_no INT, name VARCHAR(30) NOT NULL DEFAULT 'Unknown', city VARCHAR(20));
ALTER TABLE hostel_residents MODIFY name VARCHAR(50);ALTER TABLE hostel_residents CHANGE city home_city VARCHAR(40);
DESCRIBE hostel_residents;+-----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+-------------+------+-----+---------+-------+ | roll_no | int | YES | | NULL | | | name | varchar(50) | YES | | NULL | | | home_city | varchar(40) | YES | | NULL | | +-----------+-------------+------+-----+---------+-------+
What this code does
MODIFY name VARCHAR(50)made the column longer but also droppedNOT NULLand the defaultUnknown: Null is nowYESand DefaultNULL. The correct statement wasMODIFY name VARCHAR(50) NOT NULL DEFAULT 'Unknown'.CHANGE city home_city VARCHAR(40)renamed the column and widened it in one step; the type had to be written in full.
Example 5 · Shrinking a column that has data
CREATE TABLE exam_scores ( roll_no INT, name VARCHAR(30), score INT);
INSERT INTO exam_scores (roll_no, name, score) VALUES (1, 'Asha Rao', 95), (2, 'Ravi', 301);
ALTER TABLE exam_scores MODIFY name VARCHAR(20);DESCRIBE exam_scores;
ALTER TABLE exam_scores MODIFY score TINYINT;+---------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------+-------------+------+-----+---------+-------+ | roll_no | int | YES | | NULL | | | name | varchar(20) | YES | | NULL | | | score | int | YES | | NULL | | +---------+-------------+------+-----+---------+-------+ ERROR 1264 (22003): Out of range value for column 'score' at row 2
What this code does
VARCHAR(30)toVARCHAR(20)worked, because every stored name has 20 characters or fewer.- Changing
scoretoTINYINTfails with error 1264 at row 2: 301 does not fit in -128 to 127. The whole change is refused and no value is cut. Fix the bad row (301 is a typo) before you shrink the column.
Always copy the full definition from SHOW CREATE TABLE and change only what you mean to change.
MySQL converts every existing value to the new type. Making a column bigger is safe. Making it smaller fails, in strict mode, if any row does not fit, with the same errors as an insert: ERROR 1265 ... Data truncated for text that is too long, ERROR 1264 ... Out of range for numbers.
Predict the output
Predict the change
The column name is VARCHAR(30) NOT NULL DEFAULT 'Unknown'. What happens to NOT NULL?
DROP COLUMN and what happens to the data
DROP COLUMN removes a column and every value stored in it, in all rows. There is no undo: the only way back is a backup.
ALTER TABLE t DROP COLUMN name removes a column and every value stored in it, in all rows. There is no undo: the only way back is a backup. The other columns and all rows stay.
Example 6 · DROP COLUMN removes the data for good
CREATE TABLE order_lines_v2 ( order_no INT, item VARCHAR(40), coupon_code VARCHAR(12));
INSERT INTO order_lines_v2 VALUES (1, 'Notebook', 'FEST50'), (2, 'Pen', NULL);
ALTER TABLE order_lines_v2 DROP COLUMN coupon_code;SELECT * FROM order_lines_v2 ORDER BY order_no;
ALTER TABLE order_lines_v2 ADD COLUMN price DECIMAL(7,2) NOT NULL DEFAULT 0, MODIFY item VARCHAR(60) NOT NULL;DESCRIBE order_lines_v2;
ALTER TABLE order_lines_v2 DROP COLUMN coupon_code;+----------+----------+ | order_no | item | +----------+----------+ | 1 | Notebook | | 2 | Pen | +----------+----------+ +----------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+--------------+------+-----+---------+-------+ | order_no | int | YES | | NULL | | | item | varchar(60) | NO | | NULL | | | price | decimal(7,2) | NO | | 0.00 | | +----------+--------------+------+-----+---------+-------+ ERROR 1091 (42000): Can't DROP 'coupon_code'; check that column/key exists
What this code does
- After
DROP COLUMN coupon_code, the codeFEST50is gone from the database; the other columns and both rows remain. - The second
ALTER TABLEmakes two changes at once, separated by a comma. - Dropping
coupon_codeagain fails with error 1091, because the column no longer exists.
Dropping a column that does not exist gives ERROR 1091 (42000): Can't DROP 'city'; check that column/key exists. A table must keep at least one column, so dropping the last one gives error 1090 and tells you to use DROP TABLE instead.
Before dropping, check that no query or app still reads the column, and that the data is not needed for old reports. On real systems, teams often stop using a column first and drop it weeks later.
Several changes in one ALTER TABLE
One ALTER TABLE can hold several changes, separated by commas: ALTER TABLE t ADD COLUMN a INT, MODIFY b VARCHAR(80), DROP COLUMN c;. MySQL applies them together, and if one part fails, none of them is applied.
CREATE TABLE train_tickets ( pnr CHAR(10) PRIMARY KEY, passenger VARCHAR(40) NOT NULL, fare INT, coach VARCHAR(5));
ALTER TABLE train_tickets ADD COLUMN berth VARCHAR(10) NULL, MODIFY fare DECIMAL(8,2) NOT NULL, DROP COLUMN coach;
DESCRIBE train_tickets;+-----------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+--------------+------+-----+---------+-------+ | pnr | char(10) | NO | PRI | NULL | | | passenger | varchar(40) | NO | | NULL | | | fare | decimal(8,2) | NO | | NULL | | | berth | varchar(10) | YES | | NULL | | +-----------+--------------+------+-----+---------+-------+
This matters on big tables, because many changes make MySQL rebuild the table, copying every row. One statement with three changes rebuilds once instead of three times.
A safe routine for any change on real data:
- Look at the current design with
SHOW CREATE TABLE. - Try the exact statement on a copy or a test database first.
- Take a backup, then run it, and check the result with
DESCRIBEand aSELECT.
Order of execution
A safe change routine
Put the steps of a safe change on real data in order.
Next step
Check your understanding
11 quick questions