Transactions — Ensuring Data Integrity
title: “Transactions” part: 3 topic_number: 6 slug: “transactions” difficulty: “Advanced” prerequisites: “insert, update, delete” —
Transactions — Ensuring Data Integrity
What Is It?
A transaction is a sequence of SQL operations that execute as a single unit of work. Either ALL operations succeed, or NONE do — ensuring data consistency even if errors occur midway.
Real-world analogy: Bank transfer — money must leave one account AND enter another. If either step fails, both must be undone (you can’t lose money to the void!).
ACID Properties:
- Atomicity: All-or-nothing
- Consistency: Data stays valid
- Isolation: Transactions don’t interfere with each other
- Durability: Committed changes persist (even after crash)
Syntax Breakdown
1
2
3
4
5
6
START TRANSACTION; -- or BEGIN
-- SQL statements here
COMMIT; -- Save changes
-- Or if something goes wrong:
ROLLBACK; -- Undo everything since START TRANSACTION
Key commands:
- START TRANSACTION / BEGIN: Start a transaction
- COMMIT: Permanently save all changes
- ROLLBACK: Undo all changes since transaction started
- SAVEPOINT: Create a rollback point within a transaction
Basic Examples
Simple Transaction
1
2
3
4
5
6
7
8
9
10
11
12
START TRANSACTION;
UPDATE employees SET salary = salary * 1.10 WHERE department_id = 1;
-- Check if it looks right
SELECT first_name, salary FROM employees WHERE department_id = 1;
-- If good:
COMMIT;
-- If not:
-- ROLLBACK;
What happens:
- Changes are temporary until COMMIT
- ROLLBACK undoes everything
- Other sessions don’t see changes until COMMIT
Transaction with Error Handling
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
START TRANSACTION;
-- Attempt a series of operations
UPDATE employees SET salary = 100000 WHERE employee_id = 1;
UPDATE employees SET salary = 95000 WHERE employee_id = 2;
-- Simulate error check
SELECT @error := COUNT(*) FROM employees WHERE salary < 0;
-- Conditional commit/rollback
IF @error > 0 THEN
ROLLBACK;
SELECT 'Transaction rolled back due to errors';
ELSE
COMMIT;
SELECT 'Transaction committed successfully';
END IF;
Going Deeper
Bank Transfer Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
START TRANSACTION;
-- Deduct from Account A
UPDATE accounts SET balance = balance - 500 WHERE account_id = 'A';
-- Check if sufficient funds
SELECT @new_balance := balance FROM accounts WHERE account_id = 'A';
IF @new_balance < 0 THEN
ROLLBACK;
SELECT 'Insufficient funds, transaction rolled back';
ELSE
-- Add to Account B
UPDATE accounts SET balance = balance + 500 WHERE account_id = 'B';
COMMIT;
SELECT 'Transfer successful';
END IF;
Either both updates happen, or neither does.
Savepoints (Partial Rollback)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
START TRANSACTION;
INSERT INTO employees (first_name, last_name, salary) VALUES ('John', 'Doe', 70000);
SAVEPOINT sp1;
UPDATE employees SET salary = 75000 WHERE first_name = 'John' AND last_name = 'Doe';
SAVEPOINT sp2;
DELETE FROM employees WHERE salary < 60000;
-- Oops, didn't mean to delete those!
ROLLBACK TO sp2; -- Undo only the DELETE, keep INSERT and UPDATE
COMMIT;
Savepoints let you rollback to specific points without losing everything.
Transaction Isolation Levels
1
2
3
4
5
6
-- Set isolation level for current session
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
-- Your queries here
COMMIT;
Isolation levels (least to most strict):
- READ UNCOMMITTED: Can see uncommitted changes from other transactions (dirty reads)
- READ COMMITTED: Only sees committed changes (default in most databases)
- REPEATABLE READ: Same query always returns same result within transaction (MySQL default)
- SERIALIZABLE: Full isolation, transactions execute as if sequential
Multi-Table Transaction
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
START TRANSACTION;
-- Insert new department
INSERT INTO departments (department_name, location) VALUES ('R&D', 'Austin');
SET @new_dept_id = LAST_INSERT_ID();
-- Move employees to new department
UPDATE employees SET department_id = @new_dept_id WHERE department_id = 8;
-- Verify counts
SELECT @moved := COUNT(*) FROM employees WHERE department_id = @new_dept_id;
SELECT @old_count := COUNT(*) FROM employees WHERE department_id = 8;
IF @old_count = 0 AND @moved > 0 THEN
COMMIT;
SELECT CONCAT('Successfully moved ', @moved, ' employees to R&D');
ELSE
ROLLBACK;
SELECT 'Transaction failed, rolled back';
END IF;
Watch Out — Common Mistakes
Mistake #1: Forgetting to COMMIT
1
2
3
START TRANSACTION;
UPDATE employees SET salary = salary * 1.05;
-- Session ends here without COMMIT
Result: Changes are LOST! Always COMMIT explicitly.
In production: Use auto-commit OFF for explicit control, or ensure COMMIT is called.
Mistake #2: Long-Running Transactions
1
2
3
4
5
START TRANSACTION;
-- ... slow operations ...
-- ... waiting for user input ...
-- ... 10 minutes pass ...
COMMIT;
Problem: Locks held for too long, blocking other users.
Fix: Keep transactions SHORT. Don’t wait for user input inside a transaction.
Mistake #3: Mixing DDL and DML in Transactions
1
2
3
4
5
6
START TRANSACTION;
CREATE TABLE temp (id INT); -- DDL statement
INSERT INTO employees VALUES (...);
ROLLBACK; -- Won't undo the CREATE TABLE!
Issue: In MySQL, DDL statements (CREATE, ALTER, DROP) auto-commit! They can’t be rolled back.
Fix: Separate DDL from transactions, or know that DDL commits immediately.
Edge Case Spotlight
Deadlock
1
2
3
4
5
6
7
8
9
10
11
12
13
-- Session 1:
START TRANSACTION;
UPDATE employees SET salary = 100000 WHERE employee_id = 1;
-- (waiting for Session 2)
UPDATE employees SET salary = 90000 WHERE employee_id = 2;
COMMIT;
-- Session 2 (running simultaneously):
START TRANSACTION;
UPDATE employees SET salary = 95000 WHERE employee_id = 2;
-- (waiting for Session 1)
UPDATE employees SET salary = 85000 WHERE employee_id = 1;
COMMIT;
Deadlock! Each waits for the other’s lock. MySQL detects this and kills one transaction.
Prevention:
- Always update tables/rows in the same order
- Keep transactions short
- Use appropriate isolation levels
Auto-Commit Mode
1
2
3
4
SHOW VARIABLES LIKE 'autocommit'; -- Check current setting
SET autocommit = 0; -- Disable (every statement needs explicit COMMIT)
SET autocommit = 1; -- Enable (every statement auto-commits)
Default: autocommit = 1 (each statement is its own transaction)
Try This
Exercise 1 (Guided)
Create a transaction that gives a 10% raise to all employees in the Engineering department (dept_id = 1), but only if the total new payroll doesn’t exceed $300,000. If it exceeds, rollback and show a message.
Hint
START TRANSACTION, UPDATE, calculate SUM(salary), check condition, COMMIT or ROLLBACK.Exercise 2 (Independent)
Implement a “swap salaries” transaction: swap the salaries of two specific employees (IDs 1 and 2). Use savepoints to allow partial rollback if needed.
Hint
Store salaries in variables, UPDATE both employees, use SAVEPOINT, verify with SELECT, COMMIT or ROLLBACK TO savepoint.Exercise 3 (Challenge)
Create a “cascading promotion” transaction:
- Promote employee 5 to manager (set their manager_id to NULL)
- Assign all former manager’s direct reports to employee 5
- Give employee 5 a 20% raise
- If new salary exceeds $120k, rollback entirely
Hint
Multiple UPDATEs, save old manager_id, reassign reports, calculate new salary, check condition, COMMIT or ROLLBACK.Answer Key
Exercise 1 Answer
```sql START TRANSACTION; -- Give raises UPDATE employees SET salary = salary * 1.10 WHERE department_id = 1; -- Calculate new total SELECT @new_payroll := SUM(salary) FROM employees WHERE department_id = 1; -- Check limit IF @new_payroll > 300000 THEN ROLLBACK; SELECT CONCAT('Rollback: New payroll (', @new_payroll, ') exceeds limit') AS message; ELSE COMMIT; SELECT CONCAT('Success: New payroll is ', @new_payroll) AS message; END IF; ``` **Output:** Either commits with success message, or rolls back with error message.Exercise 2 Answer
```sql START TRANSACTION; -- Store current salaries SELECT @sal1 := salary FROM employees WHERE employee_id = 1; SELECT @sal2 := salary FROM employees WHERE employee_id = 2; -- Swap UPDATE employees SET salary = @sal2 WHERE employee_id = 1; SAVEPOINT after_first_update; UPDATE employees SET salary = @sal1 WHERE employee_id = 2; -- Verify SELECT @check1 := salary FROM employees WHERE employee_id = 1; SELECT @check2 := salary FROM employees WHERE employee_id = 2; IF @check1 = @sal2 AND @check2 = @sal1 THEN COMMIT; SELECT 'Salaries swapped successfully'; ELSE ROLLBACK TO after_first_update; SELECT 'Swap failed, rolled back to savepoint'; END IF; ``` **Demonstrates savepoint usage for controlled rollback.**Exercise 3 Answer
```sql START TRANSACTION; -- Store old manager SELECT @old_manager := manager_id FROM employees WHERE employee_id = 5; -- Promote employee 5 to top level UPDATE employees SET manager_id = NULL WHERE employee_id = 5; -- Reassign all reports from old manager to employee 5 UPDATE employees SET manager_id = 5 WHERE manager_id = @old_manager AND employee_id != 5; -- Give 20% raise UPDATE employees SET salary = salary * 1.20 WHERE employee_id = 5; -- Check new salary SELECT @new_salary := salary FROM employees WHERE employee_id = 5; IF @new_salary > 120000 THEN ROLLBACK; SELECT CONCAT('Promotion canceled: New salary (', @new_salary, ') exceeds cap') AS message; ELSE COMMIT; SELECT CONCAT('Promotion successful! New salary: ', @new_salary) AS message; END IF; ``` **Complex multi-step transaction with conditional logic.**Quick Recap
• Transactions ensure all-or-nothing execution (ACID)
• START TRANSACTION begins, COMMIT saves, ROLLBACK undoes
• Changes invisible to others until COMMIT
• SAVEPOINT allows partial rollback
• Keep transactions SHORT to avoid blocking
• DDL statements auto-commit (can’t be rolled back)
• Watch for deadlocks (update in consistent order)
• Use appropriate isolation levels for your needs
Part 3 Complete!
Congratulations! You’ve completed all advanced topics:
- Recursive CTEs for hierarchies
- Date/time functions
- Views for reusable logic
- Window functions (ROW_NUMBER, RANK, DENSE_RANK)
- Transactions for data integrity
You’re now a proficient MySQL developer!
Up Next
Final Capstone Challenge! → Mini Challenge 11
You’ve completed all Part 3 topics! Take on the ultimate capstone challenge combining everything you’ve learned!