skip to content

Can TRUNCATE TABLE be rolled back if it runs inside an open transaction?

level: middleimportance: should knowfreq 60%

answer

  1. The answer is not the same everywhere
  2. Depends on how the engine classifies the statement
  3. DDL in some engines means implicit commit
  4. Postgres and SQL Server behave one way
  5. MySQL and Oracle commit before and after

basics

~10 s

It depends on the engine. PostgreSQL and SQL Server treat TRUNCATE as transactional, so ROLLBACK restores the rows. MySQL and Oracle commit implicitly when TRUNCATE runs, so there is nothing left to roll back.

solid answer

~40 s

There is no single answer, and that is the point of the question. In **PostgreSQL** and **SQL Server**, `TRUNCATE TABLE` is transactional: run it inside `BEGIN ... ROLLBACK` and the rows are still there afterwards. In **MySQL** (InnoDB) and **Oracle**, `TRUNCATE` is DDL that forces an **implicit commit** — the open transaction is committed before the truncation, the truncation itself commits, and a subsequent `ROLLBACK` is a no-op that also cannot undo the earlier work in that transaction. `DELETE` has no such ambiguity: it is ordinary DML everywhere and always rolls back. So on an engine with implicit-commit DDL, a purge you might need to undo should be a `DELETE`, and any "truncate then reload" script needs to survive failing halfway rather than relying on a transaction to protect it.

code

sql · 5 lines
sql
BEGIN;
TRUNCATE TABLE staging_rows;
ROLLBACK;
-- rows are still present: TRUNCATE participated in the transaction
SELECT count(*) FROM staging_rows;

go deeper

for a junior

Know that DELETE always rolls back and that TRUNCATE's undo behaviour is not guaranteed. If unsure of the engine, say so rather than guessing a universal rule.

for a middle

Explain the mechanism: engines that classify TRUNCATE as DDL commit implicitly, so the transaction ends when the statement runs; engines that make it transactional let ROLLBACK restore the rows.

for a senior

Demonstrate the operational consequence — how you design a truncate-and-reload so a mid-load failure never leaves an empty table visible, and why savepoints are no defence on an implicit-commit engine.

for a principal

Own the portability policy: whether the codebase may use TRUNCATE at all when it must run on more than one engine, and how destructive refreshes are made recoverable without depending on engine-specific transaction semantics.

## Why this question separates candidates Almost everyone can recite "TRUNCATE is faster than DELETE". Far fewer can say what happens when a truncation sits inside a transaction that later fails — and that is the difference between a recoverable mistake and a table you have to restore from backup. The honest answer is *engine-dependent*, and stating it as a universal rule in either direction is the mistake interviewers are listening for. ## The two behaviours **Transactional TRUNCATE.** PostgreSQL and SQL Server implement `TRUNCATE TABLE` as a transactional statement. It participates in the surrounding transaction like any other write: ```sql BEGIN; TRUNCATE TABLE staging_rows; SELECT count(*) FROM staging_rows; -- 0, inside this transaction ROLLBACK; SELECT count(*) FROM staging_rows; -- the original rows are back ``` You can also truncate and reload inside one transaction, so other sessions never observe the empty intermediate state — a genuinely useful pattern for refreshing a lookup or staging table. **Implicit-commit TRUNCATE.** MySQL (InnoDB) and Oracle classify `TRUNCATE TABLE` as DDL, and DDL in those engines commits implicitly. Concretely, in MySQL: ```sql START TRANSACTION; INSERT INTO audit_marker VALUES (1); -- work in the transaction TRUNCATE TABLE staging_rows; -- implicit COMMIT happens here ROLLBACK; -- no-op ``` Two things go wrong at once. The truncation is permanent, *and* the `INSERT` that preceded it was committed by the implicit commit — so the `ROLLBACK` does not undo that either. This second effect surprises people far more than the first: a statement you thought was one step in a transaction silently ended the transaction. ## What DELETE guarantees instead `DELETE` is DML in every engine. It never commits implicitly, it always rolls back, and it always leaves the enclosing transaction intact: ```sql BEGIN; DELETE FROM staging_rows; ROLLBACK; -- rows restored, everywhere, no exceptions ``` That universality is the reason to choose `DELETE` for a destructive operation you might need to abort, even when the table is large enough that `TRUNCATE` would be much faster. ## Savepoints do not rescue you A natural follow-up thought is "I will wrap the truncate in a savepoint". On an implicit-commit engine that does not help: the commit happens when the `TRUNCATE` executes, and once a transaction has been committed there are no savepoints left to roll back to. Savepoints only ever undo work inside a still-open transaction. ## Practical consequences for scripts If you write an ETL or migration script that does *truncate-then-reload*, ask which engine it will run on. - On PostgreSQL or SQL Server you can make the whole refresh atomic: `BEGIN; TRUNCATE TABLE dim_customer; INSERT INTO dim_customer SELECT ...; COMMIT;` — a failure mid-load leaves the old data in place. - On MySQL or Oracle you cannot. The instant the truncation runs the table is empty and committed, so a failed load leaves an empty table visible to everyone. Mitigations are structural rather than transactional: load into a new table and swap names, or use `DELETE` so the whole refresh really is one transaction. Also remember that a truncation you *can* roll back is still not free of consequences — an identity counter that was restarted, or a statement-level truncate trigger that already ran and wrote somewhere outside the database, may not be undone by the rollback in the way you assume. Rollback restores table contents; it does not un-send side effects that left the database. ## How to answer in an interview Say "it depends on the engine", then name which engines fall on each side and why: transactional versus DDL-with-implicit-commit. Add the practical rule — if you might need to undo it, use `DELETE`, and never assume a `TRUNCATE` you did not test is safe inside your transaction. Volunteering the implicit-commit side effect on preceding statements is the detail that reads as real experience.

  • Does wrapping the TRUNCATE in a SAVEPOINT help on an engine that commits implicitly?
    No. The implicit commit fires when the TRUNCATE executes, ending the transaction and discarding every savepoint in it. There is no open transaction left to roll back to. Savepoints only undo work inside a transaction that is still open, so they cannot protect against a statement whose very execution commits.
  • What does the implicit commit do to statements that ran earlier in the same transaction?
    It commits them. On MySQL or Oracle, an INSERT or UPDATE issued before the TRUNCATE becomes permanent the moment the TRUNCATE runs, so a later ROLLBACK cannot undo it either. This is the subtler hazard: a single DDL statement silently converts everything before it into committed work.
  • How would you write an atomic truncate-and-reload on an engine where TRUNCATE commits implicitly?
    Do not rely on a transaction. Load the new data into a separate table and swap it in by renaming, or use DELETE instead of TRUNCATE so the whole refresh is genuine DML inside one transaction. The tradeoff is DELETE's cost on large tables versus the extra table and rename step.

saying these in an interview costs you the question

  • States flatly that TRUNCATE can never be rolled back
  • States flatly that TRUNCATE is always transactional
  • Thinks a SAVEPOINT protects against an implicit commit
  • Unaware that implicit commit also commits earlier statements
  • Assumes DELETE might also commit implicitly on some engines

context