Chapter 10
Chapter 10 — Transactions and the CASE Expression
This final chapter brings together two topics that sit at the heart of production SQL. Transactions let you group multiple statements into a single all-or-nothing unit — essential any time one logical operation requires several writes. The CASE expression brings conditional logic directly into your queries, powering everything from status labels to conditional aggregation.
Part 1 — Transactions
Why transactions exist
Imagine a customer buys a book. You need to do two things: insert a row into
orders and decrement stock in books. If the
server crashes or an error occurs between the two statements, you end up with an order
but no stock reduction — or vice versa. The database is now inconsistent.
A transaction makes those two writes atomic: either both happen, or neither does. The database moves from one consistent state to another without any in-between half-state being visible to other users.
All statements in a transaction succeed together, or none of them do. There is no partial commit.
A transaction always moves the database from one valid state to another. Constraints and rules are enforced at commit.
Concurrent transactions don't see each other's uncommitted changes. The level of isolation is configurable.
Once committed, a transaction's changes survive a crash. InnoDB writes to the redo log before confirming success.
The transaction statements
A complete purchase transaction
ordersbooks to reduce stock by the quantity orderedSAVEPOINT — partial rollbacks
Within a transaction you can plant named savepoints. Rolling back to a savepoint undoes everything after it while keeping everything before it. The transaction remains open — you can continue adding more statements or commit/rollback the whole thing:
Autocommit — the default behaviour
By default MySQL runs in autocommit mode — every statement is
automatically wrapped in its own transaction and committed immediately. This is why
a bare DELETE FROM orders WHERE order_id = 5 takes effect straight away
with no COMMIT needed.
| Mode | How it works | When to use |
|---|---|---|
| Autocommit ON (default) | Each statement commits immediately. You can still use START TRANSACTION to open a multi-statement transaction — autocommit is suspended until COMMIT or ROLLBACK. | Most application code — quick one-off queries, reads, single-table inserts. |
| Autocommit OFF | SET autocommit = 0 — every statement is implicitly inside a transaction. You must COMMIT manually or changes are lost when the session closes. |
Rarely used directly. Some ORMs and connection pools manage this for you. |
CREATE TABLE, ALTER TABLE, and DROP TABLE
implicitly commit the current transaction before executing. You cannot roll back a
CREATE or DROP — even inside an explicit transaction. Always issue DDL outside of
data-change transactions, and double-check before running in production.
Part 2 — The CASE Expression
CASE is SQL's way of writing if-then-else logic inside a query. It's an expression — it returns a value — so it can go anywhere a value is valid: the SELECT list, ORDER BY, WHERE, HAVING, and even inside aggregate functions.
Searched CASE (the common form)
Simple CASE (equality only)
Stock status labels
| title | stock | stock_status |
|---|---|---|
| The Left Hand of Darkness | 9 | Low stock |
| Reasons to Stay Alive | 18 | In stock |
| Dune | 27 | In stock |
| Sapiens | 31 | Well stocked |
| The Midnight Library | 42 | Well stocked |
| And Then There Were None | 55 | Well stocked |
CASE in ORDER BY — custom sort order
Simple CASE — mapping a code to a label
3. Conditional Aggregation
One of the most powerful patterns in SQL: put a CASE expression inside an aggregate function to aggregate only the rows that match a condition. This lets you pivot rows into columns, compute multiple metrics in a single pass, and avoid repeating GROUP BY queries.
Counting by category in one query
| out_of_stock | low_stock | in_stock |
|---|---|---|
| 0 | 1 | 5 |
The CASE returns 1 when the condition matches and NULL otherwise. COUNT ignores NULLs — so it counts only the matching rows. No ELSE needed: the implicit ELSE is NULL, which COUNT skips.
Revenue split by genre in one row
| fiction_rev | mystery_rev | nonfiction_rev | scifi_rev | total_rev |
|---|---|---|---|---|
| 17.98 | 20.97 | 18.98 | 9.99 | 67.92 |
Four different SUM calculations in a single table scan — no UNION or subqueries needed. This "pivot" pattern is extremely common in reporting queries.
Conditional aggregation with HAVING
| author | total_books | low_stock_titles |
|---|---|---|
| Ursula Le Guin | 1 | 1 |
Using CASE in UPDATE — conditional bulk update
Chapter Summary
| Concept | Key points |
|---|---|
| START TRANSACTION / BEGIN | Opens an explicit transaction. Autocommit is suspended until COMMIT or ROLLBACK. |
| COMMIT | Makes all changes in the transaction permanent and visible to other sessions. |
| ROLLBACK | Undoes all changes since START TRANSACTION. Leaves the database as if the transaction never happened. |
| SAVEPOINT / ROLLBACK TO | Named checkpoints within a transaction for partial rollbacks. The transaction stays open after ROLLBACK TO. |
| ACID | Atomicity (all or nothing), Consistency (valid state to valid state), Isolation (concurrent transactions don't interfere), Durability (committed changes survive crashes). |
| Autocommit | On by default — each bare statement commits immediately. START TRANSACTION temporarily disables it for that block. |
| DDL and transactions | CREATE TABLE, ALTER TABLE, DROP TABLE implicitly commit the current transaction. Cannot be rolled back. |
| Searched CASE | CASE WHEN condition THEN result … ELSE default END. First matching WHEN wins. ELSE is optional — no match without ELSE returns NULL. |
| Simple CASE | CASE col WHEN val THEN result … END. Tests equality only. Shorter syntax for fixed value mappings. |
| Conditional aggregation | CASE inside SUM/COUNT/AVG. CASE returns a value or NULL; aggregate skips NULLs — effectively aggregating only matching rows. Powerful pivot pattern. |