Database transactions and ACID properties

Maria Garcia Feb 2026
2 tabs
-- Basic transaction
BEGIN;

UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

-- If both succeed, commit
COMMIT;

-- If error occurs, rollback
-- ROLLBACK;

-- Transaction with error handling (PostgreSQL)
DO $$
BEGIN
  BEGIN
    UPDATE accounts SET balance = balance - 100 WHERE id = 1;
    UPDATE accounts SET balance = balance + 100 WHERE id = 2;

    -- Check business rule
    IF (SELECT balance FROM accounts WHERE id = 1) < 0 THEN
      RAISE EXCEPTION 'Insufficient funds';
    END IF;

  EXCEPTION
    WHEN OTHERS THEN
      RAISE NOTICE 'Transaction failed: %', SQLERRM;
      ROLLBACK;
  END;
END $$;

-- Savepoints for partial rollback
BEGIN;

INSERT INTO orders (user_id, total) VALUES (1, 100);
SAVEPOINT after_order;

INSERT INTO order_items (order_id, product_id, quantity)
VALUES (1, 101, 2);
SAVEPOINT after_items;

-- Oops, wrong quantity
ROLLBACK TO SAVEPOINT after_items;

-- Try again
INSERT INTO order_items (order_id, product_id, quantity)
VALUES (1, 101, 5);

COMMIT;

-- Read phenomena and isolation levels

-- Dirty Read: Reading uncommitted changes
-- Transaction 1:
BEGIN;
UPDATE products SET price = 200 WHERE id = 1;
-- Not committed yet

-- Transaction 2 (with Read Uncommitted):
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT price FROM products WHERE id = 1;  -- Sees 200 (dirty read)

-- Non-repeatable Read: Same query, different results
-- Transaction 1:
BEGIN;
SELECT price FROM products WHERE id = 1;  -- Gets 100
-- ... time passes ...
SELECT price FROM products WHERE id = 1;  -- Gets 200!

-- Transaction 2 (committed between reads):
UPDATE products SET price = 200 WHERE id = 1;
COMMIT;

-- Phantom Read: New rows appear
-- Transaction 1:
BEGIN;
SELECT COUNT(*) FROM orders WHERE status = 'pending';  -- Gets 5
-- ... time passes ...
SELECT COUNT(*) FROM orders WHERE status = 'pending';  -- Gets 6!

-- Transaction 2 (inserted between reads):
INSERT INTO orders (status) VALUES ('pending');
COMMIT;
2 files · sql Explain with highlit

Transactions ensure data consistency through ACID properties. Atomicity guarantees all-or-nothing execution. Consistency maintains database constraints. Isolation prevents concurrent transaction interference. Durability persists committed changes. I use transactions for multi-step operations requiring consistency. Isolation levels—Read Uncommitted, Read Committed, Repeatable Read, Serializable—balance consistency and performance. Read Committed is default for most databases. Serializable provides highest isolation but lowest concurrency. Deadlocks occur when transactions wait circularly—database rolls one back. SAVEPOINT enables partial rollback. Two-phase commit coordinates distributed transactions. Understanding transaction isolation prevents race conditions and data corruption. Proper transaction scoping is critical for data integrity.