TRUNCATE TABLE in PostgreSQL

Learn how to use PostgreSQL TRUNCATE TABLE to remove all rows quickly, reset identity sequences with RESTART IDENTITY, and handle foreign key references using CASCADE.

5 min read · Back to overview

Quick Answer

TRUNCATE TABLE removes all rows by deallocating data pages, making it far faster than DELETE with no WHERE clause. It is transaction-safe, does not fire ON DELETE triggers, and can reset identity sequences with RESTART IDENTITY. Use CASCADE when the table has foreign key references.

Spin up a Postgres database in 20 seconds with Vela.

Try Vela Sandbox

TRUNCATE TABLE removes all rows from a table by deallocating entire data pages, rather than deleting rows one by one. Unlike DELETE, it does not scan rows, does not fire ON DELETE triggers, and leaves no dead tuples for vacuum to clean up. The table structure, indexes, and constraints remain intact after truncation.

Syntax

TRUNCATE TABLE table_name
  [RESTART IDENTITY | CONTINUE IDENTITY]
  [CASCADE | RESTRICT];

Truncate multiple tables in one statement:

TRUNCATE TABLE orders, order_lines;

Reset associated identity or SERIAL sequences:

TRUNCATE TABLE products RESTART IDENTITY;

The default is CONTINUE IDENTITY, which leaves sequence values unchanged.

Practical Example

Set up a products table with some data:

CREATE TABLE products (
  product_id  SERIAL PRIMARY KEY,
  sku         VARCHAR(50) NOT NULL UNIQUE,
  name        VARCHAR(200) NOT NULL,
  unit_price  NUMERIC(10,2) NOT NULL
);

INSERT INTO products (sku, name, unit_price)
VALUES
  ('WID-001', 'Basic Widget',   9.99),
  ('WID-002', 'Premium Widget', 24.99),
  ('WID-003', 'Deluxe Widget',  49.99);

Clear all rows and reset the sequence:

TRUNCATE TABLE products RESTART IDENTITY;

-- Verify the table is empty
SELECT COUNT(*) FROM products;
-- 0

-- Next insert gets product_id = 1 again
INSERT INTO products (sku, name, unit_price)
VALUES ('WID-NEW', 'New Widget', 14.99)
RETURNING product_id;
-- product_id = 1

Handle a parent table with foreign key references:

CREATE TABLE orders (
  order_id    SERIAL PRIMARY KEY,
  placed_at   TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE order_lines (
  line_id    SERIAL PRIMARY KEY,
  order_id   INT NOT NULL,
  product_id INT NOT NULL,
  quantity   INT NOT NULL,
  FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE
);

-- Attempting to truncate the parent alone raises an error:
TRUNCATE TABLE orders;
-- ERROR: cannot truncate a table referenced in a foreign key constraint
-- HINT: Truncate table "order_lines" at the same time, or use TRUNCATE ... CASCADE.

-- Use CASCADE to truncate both:
TRUNCATE TABLE orders CASCADE;
-- NOTICE: truncate cascades to table "order_lines"

Wrap in a transaction for safe testing:

BEGIN;
TRUNCATE TABLE order_lines, orders RESTART IDENTITY;
-- Verify before committing
SELECT COUNT(*) FROM orders;
COMMIT;  -- or ROLLBACK to undo

TRUNCATE vs DELETE

FeatureTRUNCATEDELETE (no WHERE)
Speed on large tablesVery fastSlow (row-by-row)
WAL volumeMinimalHigh
ON DELETE trigger firesNoYes
Supports WHERE clauseNoYes
TransactionalYesYes
Resets sequencesYes (RESTART IDENTITY)No
Generates dead tuplesNoYes (needs vacuum)

Testing with Vela

Before truncating production tables — especially with CASCADE — Vela database branching lets you run the TRUNCATE on a copy of the live database to see exactly which tables are affected and verify the RESTART IDENTITY behavior. Test your reload pipeline immediately after to confirm new inserts work correctly, then apply to production with confidence.

Production Tips

  • Use TRUNCATE instead of DELETE with no WHERE clause when clearing large tables — it is orders of magnitude faster and produces far less WAL traffic.
  • Add RESTART IDENTITY when you want new inserts after the truncate to start from ID 1 (or the sequence start value).
  • Use CASCADE carefully — verify the full cascade list with \d+ table_name before running in production.
  • Wrap destructive truncates in a BEGIN / COMMIT block so you can ROLLBACK if something unexpected is affected.
  • TRUNCATE does not fire row-level ON DELETE triggers — if trigger logic must run, use DELETE instead.
  • Because TRUNCATE generates minimal WAL, it can be much faster than DELETE even on tables with moderate row counts.

Continue in Managing Tables: Copy Table.

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

Frequently Asked Questions

What does TRUNCATE TABLE do in PostgreSQL?
TRUNCATE TABLE removes all rows from a table by deallocating its data pages, without scanning or logging each row individually. The table structure, indexes, and constraints remain intact. It is orders of magnitude faster than DELETE for clearing large tables.
Does TRUNCATE TABLE lock the table and block queries?
Yes. TRUNCATE acquires an ACCESS EXCLUSIVE lock on the table (and any tables truncated by CASCADE), blocking concurrent reads and writes. However, because TRUNCATE completes very quickly, the lock is typically held for a much shorter time than a full-table DELETE.
What happens to foreign key references when I truncate a table?
If other tables have foreign key constraints pointing to the table being truncated, PostgreSQL raises an error by default. Use CASCADE to truncate the parent table and all referencing tables simultaneously: TRUNCATE TABLE orders CASCADE.
Can I use TRUNCATE TABLE inside a transaction?
Yes. TRUNCATE is fully transactional in PostgreSQL. Wrap it in a BEGIN/COMMIT block and ROLLBACK to undo the operation if needed. This is one of TRUNCATE's advantages over DROP TABLE in recovery scenarios.
What is the safest way to truncate and reset sequences in production?
Use TRUNCATE TABLE table_name RESTART IDENTITY CASCADE inside a transaction. Test on a Vela branch first to confirm which tables are affected by CASCADE. RESTART IDENTITY resets all associated sequences to their starting values, preventing gaps in new IDs after the reload.