Altering Tables
Schemas change as an application grows, and ALTER TABLE is the tool for evolving a table after it already has data in it — adding or dropping columns, changing a column's type, renaming things, and adding or dropping constraints. Each of these is a distinct sub-command run against the same ALTER TABLE statement.
Adding a column
ADD COLUMN appends a new column to an existing table. If existing rows need a value for it, give it a DEFAULT — otherwise every existing row gets NULL for the new column.
Adding a discount column to products
ALTER TABLE products ADD COLUMN discount_pct NUMERIC(5, 2) NOT NULL DEFAULT 0;
Dropping a column
DROP COLUMN removes a column and every value stored in it, in one statement.
Removing an unused column
ALTER TABLE products DROP COLUMN description;
Changing a column's type
ALTER COLUMN ... TYPE changes the declared type of a column. For simple, compatible conversions (widening an INTEGER to a BIGINT, for instance), PostgreSQL figures out the conversion on its own. For anything less obvious, it needs to be told explicitly how to turn the old values into the new type, using a USING clause.
Converting a text column to numeric
ALTER TABLE products ALTER COLUMN unit_price TYPE NUMERIC(10, 2) USING unit_price::NUMERIC(10, 2);
Renaming a column or table
Renaming
ALTER TABLE products RENAME COLUMN unit_price TO price; ALTER TABLE products RENAME TO catalog_products;
ALTER TABLE ALTER TABLE
Adding and dropping constraints
Adding a CHECK constraint, then removing it
ALTER TABLE products ADD CONSTRAINT price_non_negative CHECK (price >= 0); ALTER TABLE products DROP CONSTRAINT price_non_negative;
Metadata-only changes vs. table rewrites
Not all ALTER TABLE operations cost the same. Some are pure metadata changes that complete almost instantly no matter how large the table is; others require PostgreSQL to rewrite every row of the table, which can take a long time and, worse, hold a lock that blocks other queries for the duration.
Operation | Typical cost |
|---|---|
ADD COLUMN with no default, or a constant default (PG 11+) | Metadata-only — fast |
RENAME COLUMN / RENAME TABLE | Metadata-only — fast |
DROP COLUMN | Metadata-only (data is hidden, reclaimed later by VACUUM) |
ALTER COLUMN ... TYPE (incompatible types) | Full table rewrite — slow, holds a lock |
ADD COLUMN with a volatile default (e.g. random()) | Full table rewrite |
ADD CONSTRAINT ... CHECK / NOT NULL on existing data | Full table scan to validate, though not always a rewrite |
ADD COLUMN and DROP COLUMN change a table's shape; dropping a column deletes its data permanently.
ALTER COLUMN ... TYPE may need an explicit USING clause to convert existing values.
RENAME COLUMN and RENAME TABLE just relabel things.
Some ALTER TABLE operations are fast metadata changes; others rewrite the whole table and lock it — know which kind you are running before doing it on a large production table.