BEGIN / COMMIT / ROLLBACK
BEGIN opens a transaction; COMMIT makes every change inside it permanent and visible to other sessions; ROLLBACK discards every change as if the transaction never ran. Until you commit, your changes live in your session only: invisible to everyone else, undoable in one statement.
Syntax #
BEGIN; -- or START TRANSACTION;
-- one or more DML statements
UPDATE users SET status = 'archived' WHERE last_seen_at < DATE '2025-01-01';
DELETE FROM sessions WHERE user_id IN (SELECT id FROM users WHERE status = 'archived');
COMMIT; -- make permanent
-- ROLLBACK; -- ...or undo everything since BEGIN
-- Savepoint: nested rollback boundary.
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
SAVEPOINT before_credit;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- if the credit fails:
ROLLBACK TO SAVEPOINT before_credit;
-- ...handle error, try alternate path...
COMMIT;Example #
Isolation levels #
Isolation controls what other concurrent transactions see while yours is in flight, and how they interfere with you. Four standard levels, weakest to strongest:
READ UNCOMMITTED: see other transactions' uncommitted changes ("dirty reads"). Rare in practice; some engines don't even implement it.READ COMMITTED(default in Postgres, Oracle, SQL Server): only see committed changes. Same query inside one transaction can return different results if another transaction commits in between ("non-repeatable read").REPEATABLE READ(default in MySQL InnoDB): snapshot at the moment of the first read; same query returns the same rows. New rows from other transactions can still appear ("phantom read") on some engines.SERIALIZABLE: behaves as if transactions ran one at a time. Highest correctness, lowest concurrency, occasional retries when the engine detects a serialization conflict.
Set per session or per transaction:
-- Per transaction.
BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- ...
COMMIT;
-- Per session (Postgres / SQL Server).
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ;Dialect notes #
- Auto-commit by default: every engine starts each statement in implicit auto-commit mode.
BEGINis what disables it for the next block of statements. - Postgres:
BEGINis the standard. Default isolationREAD COMMITTED. Both DDL and DML are transactional. - MySQL InnoDB:
START TRANSACTIONorBEGIN. Default isolationREPEATABLE READ. DDL implicitly commits:CREATE TABLEinside a transaction will auto-commit any pending DML before it. Don't mix DDL into a DML transaction on MySQL. - SQL Server:
BEGIN TRANSACTION(orBEGIN TRAN). DefaultREAD COMMITTED. Full DDL-in-transaction support. Watch forXACT_ABORTsettings. - Oracle:
BEGINis implicit, so every statement opens a transaction;COMMITorROLLBACKcloses it. DDL implicitly commits. - SQLite:
BEGIN/COMMIT/ROLLBACK. Single-writer at a time.SAVEPOINTsupported. - Warehouses (Snowflake, BigQuery, Redshift): transactions exist but the workload patterns differ — large
MERGEandINSERT OVERWRITEoperations are usually atomic on their own.BEGIN/COMMITblocks are rarer than in OLTP databases.
The "BEGIN / verify / COMMIT" pattern is the single highest-leverage habit when running ad-hoc DML against production. BEGIN; UPDATE ... WHERE ...; SELECT * FROM ... WHERE ...; — eyeball the result. If it's right, COMMIT;. If it's wrong, ROLLBACK; and start over. Costs you 30 seconds per change. Saves you the day a typo would have rewritten 100,000 rows. (Note: some psql / DBeaver settings auto-commit each statement — confirm yours don't, or you'll be in for a surprise.)