DELETE removes one or more rows from a PostgreSQL table according to an optional WHERE condition.
DELETE quickly removes unwanted data, reclaims storage, and keeps tables clean. Pair it with WHERE to target only the rows you need.
Use DELETE FROM table_name WHERE condition;
. Omit WHERE to delete every row, or add RETURNING
to see affected rows.
Filter by a unique column.
DELETE FROM users WHERE id = 42 RETURNING *;
Combine predicates with AND/OR.
DELETE FROM orders
WHERE status = 'expired' AND created_at < NOW() - INTERVAL '30 days';
JOIN the target and source tables.
DELETE FROM sessions s
USING users u
WHERE s.user_id = u.id AND u.disabled = true;
Use TRUNCATE table_name;
for a bulk, logging-minimal operation. It is faster than DELETE without WHERE but cannot be rolled back across different sessions until commit.
Batch with LIMIT
or CTE
.
WITH del AS (
DELETE FROM events
WHERE processed = true
LIMIT 1000
RETURNING 1)
SELECT count(*) FROM del;
1) Always test with SELECT first.
2) Wrap in a transaction so you can ROLLBACK.
3) Keep indexes on filter columns for speed.
4) Use CASCADE rules to manage child rows.
Deleting without WHERE, ignoring foreign-key constraints, and running massive deletes in a single transaction that bloat WAL or lock tables.
Not entirely. Rows become dead tuples, reclaimed later by autovacuum. To force space reuse, run VACUUM (FULL)
or use TRUNCATE
.
Yes. DELETE logs each removed row and fires triggers, while TRUNCATE is bulk and almost instantaneous but less granular and cannot activate row-level triggers.
Yes, if executed inside an uncommitted transaction. Issue ROLLBACK
before commit to restore the data.