Drop Column in PostgreSQL

Learn how to use ALTER TABLE DROP COLUMN to remove columns from a PostgreSQL table, handle dependent objects with CASCADE, and reclaim disk space after dropping.

4 min read · Back to overview

Quick Answer

Use ALTER TABLE table_name DROP COLUMN column_name to remove a column. PostgreSQL automatically removes indexes and constraints on the dropped column. Use IF EXISTS to suppress errors, CASCADE to also drop dependent views and functions, and VACUUM FULL to reclaim disk space afterward.

Spin up a Postgres database in 20 seconds with Vela.

Try Vela Sandbox

ALTER TABLE ... DROP COLUMN removes one or more columns from an existing table. PostgreSQL automatically removes single-column indexes and constraints on the dropped column. If other objects — views, triggers, stored functions — reference the column, you must either update those objects first or use CASCADE to drop them along with the column.

Syntax

-- Drop a single column
ALTER TABLE table_name DROP COLUMN column_name;

-- Drop only if it exists (no error if missing)
ALTER TABLE table_name DROP COLUMN IF EXISTS column_name;

-- Drop and cascade to dependent objects
ALTER TABLE table_name DROP COLUMN column_name CASCADE;

-- Drop multiple columns at once
ALTER TABLE table_name
  DROP COLUMN col1,
  DROP COLUMN col2;

Practical Example

Set up tables and a view with column dependencies:

CREATE TABLE categories (
  category_id  SERIAL PRIMARY KEY,
  name         VARCHAR(100) NOT NULL
);

CREATE TABLE products (
  product_id   SERIAL PRIMARY KEY,
  name         VARCHAR(200) NOT NULL,
  category_id  INT NOT NULL REFERENCES categories(category_id),
  internal_ref VARCHAR(50),
  notes        TEXT,
  barcode      VARCHAR(50)
);

CREATE VIEW product_catalog AS
  SELECT p.product_id, p.name, c.name AS category_name, p.barcode
  FROM products p
  JOIN categories c USING (category_id);

Drop a column that has a foreign key constraint — the constraint is removed automatically:

ALTER TABLE products DROP COLUMN IF EXISTS category_id;

Attempt to drop a column referenced by the view (fails without CASCADE):

ALTER TABLE products DROP COLUMN barcode;
-- ERROR: cannot drop table products column barcode because other objects depend on it
-- DETAIL: view product_catalog depends on table products column barcode
-- HINT: Use DROP ... CASCADE to drop the dependent objects too.

Use CASCADE to drop the column and the dependent view:

ALTER TABLE products DROP COLUMN barcode CASCADE;
-- NOTICE: drop cascades to view product_catalog

Drop multiple columns simultaneously (one lock acquisition):

ALTER TABLE products
  DROP COLUMN internal_ref,
  DROP COLUMN notes;

Confirm the final structure:

\d products

Reclaim disk space after dropping large columns:

VACUUM FULL products;

Disk Space and VACUUM

Dropping a column marks its data as invisible but does not immediately free the storage. The space is reclaimed by autovacuum over time, or immediately by VACUUM FULL. Note that VACUUM FULL requires an exclusive lock and rewrites the table, so schedule it during a maintenance window for large tables.

-- Check bloat before and after
SELECT
  relname,
  pg_size_pretty(pg_total_relation_size(oid)) AS total_size
FROM pg_class
WHERE relname = 'products';

Testing with Vela

Before dropping columns in production, Vela database branching lets you run the operation on a production-data clone to confirm exactly which dependent objects will be cascaded, measure how long VACUUM FULL takes to reclaim space, and verify that application queries against the updated schema still work correctly.

Production Tips

  • Dropping a column does not immediately reclaim disk space — run vacuum or VACUUM FULL to reclaim it, keeping in mind VACUUM FULL requires an exclusive lock.
  • Use IF EXISTS in deployment scripts to make them idempotent — the statement becomes a no-op if the column was already dropped.
  • Review dependencies with \d+ table_name or query pg_depend before adding CASCADE — it drops all referencing views, functions, and rules.
  • Drop multiple columns in one ALTER TABLE statement to minimize the number of lock acquisitions.
  • For large tables, consider whether dropping the column is urgent or can be deferred to a maintenance window that also runs VACUUM FULL.

Continue in Managing Tables: Rename Column.

Related in this section: PostgreSQL Data Types · Create Table · Select Into

Frequently Asked Questions

What does DROP COLUMN do in PostgreSQL?
DROP COLUMN removes a column definition from the table and marks existing row data for that column as invisible. Indexes and constraints that applied solely to the dropped column are automatically removed. The column data is not immediately reclaimed on disk until the table is vacuumed or rewritten.
Does DROP COLUMN lock the table and block queries?
Yes. DROP COLUMN acquires an ACCESS EXCLUSIVE lock, blocking concurrent reads and writes. The operation itself is fast (it marks the column invisible without rewriting the table), so the lock is brief in most cases.
What happens to dependent objects when I drop a column?
Single-column indexes and constraints on the dropped column are removed automatically. If a view, function, or trigger references the column, PostgreSQL raises an error unless you use CASCADE, which drops all dependent objects along with the column.
Can I use IF EXISTS with DROP COLUMN?
Yes. ALTER TABLE table_name DROP COLUMN IF EXISTS column_name issues a NOTICE rather than an error when the column does not exist. This makes migration scripts idempotent and safe to re-run.
What is the safest way to drop a column in production?
Use IF EXISTS in scripts. Check for dependent objects with \d+ table_name or pg_depend before adding CASCADE. Drop the column during a low-traffic window and schedule a VACUUM FULL during a maintenance window to reclaim the disk space.