Synopsis
Use the TRUNCATE
statement to remove all rows from the specified table or, optionally, to remove all rows from all the tables in the closure with foreign key references to the specified table.
Applying TRUNCATE
to a set of tables produces the same ultimate outcome as does using an unrestricted DELETE
on each table in the set; but it doesn't scan the tables. TRUNCATE
is therefore faster than DELETE
. It also reclaims disk space immediately. This means that the larger is the table, the more greater the performance benefit of TRUNCATE
is.
Syntax
Table inheritance is not yet supported
The table_expr rule specifies syntax that is useful only when at least one other table inherits one of the tables that thetruncate
statement lists explicitly. See this note for more detail. Until inheritance is supported, use a bare table_name.
Truncate is not transactional
TRUNCATE
in the current implementation is not transactional.
You should avoid using TRUNCATE in the following circumstances:
- inside of a multi-step transaction
- concurrently with read and write operations in the same table
If your use case is mostly for CI/CD on smaller datasets, you can use DELETE FROM table;
.
This is heavier weight (and also not recommended for very large data sets) but will be transactional.
Note that even in PostgreSQL truncate is not MVCC-safe.
Semantics
Specify the name of the table to be truncated.
TRUNCATE
acquiresACCESS EXCLUSIVE
lock on the tables to be truncated. TheACCESS EXCLUSIVE
locking option is not yet fully supported.TRUNCATE
is not supported for foreign tables.CASCADE
andRESTRICT
affect what happens when the table that theTRUNCATE
statement targets has dependent tables. A dependent table (and, in turn, its dependent tables) have that status because they have direct or transitive foreign key constrains to the target table.CASCADE
causes the closure of dependent tables all to be truncated. AndRESTRICT
causes theTRUNCATE
attempt to fail if the target table has any dependent tables. This error outcome is the same even when all of the tables are empty. If neitherCASCADE
norRESTRICT
is written, then the effect is as ifRESTRICT
had been written.
Example
First create a parent-child table pair and populate them:
drop table if exists children cascade;
drop table if exists parents cascade;
create table parents(k int primary key, v text not null);
create table children(
parent_k int not null,
k int not null,
v text not null,
constraint children_pk primary key(parent_k, k),
constraint children_fk foreign key(parent_k)
references parents(k)
match full
on delete cascade
on update restrict);
insert into parents(k, v) values (1, 'dog'), (2, 'cat'), (3, 'frog');
insert into children(parent_k, k, v) values
(1, 1, 'dog-child-a'),
(1, 2, 'dog-child-b'),
(1, 3, 'dog-child-c'),
(2, 1, 'cat-child-a'),
(2, 2, 'cat-child-b'),
(2, 3, 'cat-child-c'),
(3, 1, 'frog-child-a'),
(3, 2, 'frog-child-b'),
(3, 3, 'frog-child-c');
select p.v as "parents.v", c.v as "children.v"
from parents p inner join children c on c.parent_k = p.k
order by p.k, c.k;
This is the result:
parents.v | children.v
-----------+--------------
dog | dog-child-a
dog | dog-child-b
dog | dog-child-c
cat | cat-child-a
cat | cat-child-b
cat | cat-child-c
frog | frog-child-a
frog | frog-child-b
frog | frog-child-c
The \d children
meta-command shows that it has a foreign key constraint to the parents
table. This makes it a (transitive) dependent object of that table:
Indexes:
"children_pk" PRIMARY KEY, lsm (parent_k HASH, k ASC)
Foreign-key constraints:
"children_fk" FOREIGN KEY (parent_k) REFERENCES parents(k) MATCH FULL ON UPDATE RESTRICT ON DELETE CASCADE
Notice that the effect of the on delete cascade
clause is limited to what the delete
statement does. It has no effect on the behavior of truncate
. (There is no on truncate cascade
clause.) Try delete from parents
. It quietly succeeds and removes all the rows from both the parents
table and the children
table.
With all the rows that the setup code above inserts, try this:
do $body$
declare
message text not null := '';
detail text not null := '';
begin
-- Causes error 'cos "cascade" is required.
truncate table parents;
assert false, 'Should not get here';
exception
-- Error 0A000
when feature_not_supported then
get stacked diagnostics
message = message_text,
detail = pg_exception_detail;
assert message = 'cannot truncate a table referenced in a foreign key constraint', 'Bad message';
assert detail = 'Table "children" references "parents".', 'Bad detail';
end;
$body$;
It finishes without error, showing that the bare truncate table parents
, without cascade
, fails and causes the message and hint that the code presents. Now repeat the attempt with cascade
and observe the result:
truncate table parents cascade;
select
(select count(*) from parents) as "parents count",
(select count(*) from children) as "children count";
The truncate
statement now finishes without error. This is the result:
parents count | children count
---------------+----------------
0 | 0
Finally, try truncate table parents
again. As promised, it still fails with the 0A000 error, even though the transitively dependent table, children, is empty.