CASE Expressions

CASE is SQL's if/else. It picks a value based on conditions, row by row, and can be used anywhere a value can: in the select list, in ORDER BY, inside aggregates and in updates. It's how you turn raw data into labels, buckets and custom sort orders.

Searched CASE

SQL
SELECT name, price,
       CASE
         WHEN price < 20 THEN 'Budget'
         WHEN price < 50 THEN 'Standard'
         ELSE 'Premium'
       END AS price_band
FROM products
ORDER BY price;
Result 12 rows
namepriceprice_band
Ceramic Mug12.5Budget
Smart Plug14.99Budget
Jigsaw Puzzle 100018Budget
USB-C Charger19.99Budget
The Patient Gardener24.9Standard
Learning SQL Step by Step32.5Standard
Desk Lamp34Standard
Wooden Train Set39.9Standard
Night Sky Atlas45Standard
Linen Throw49Standard
Wireless Earbuds59.99Premium
Mechanical Keyboard89Premium

Conditions are checked in order, and the first true one wins — so the second WHEN only sees prices of 20 or more. If nothing matches and there's no ELSE, the result is NULL.

Simple CASE

SQL
SELECT id, status,
       CASE status
         WHEN 'pending' THEN 'Awaiting dispatch'
         WHEN 'shipped' THEN 'On its way'
         WHEN 'delivered' THEN 'Complete'
         ELSE 'Cancelled'
       END AS customer_message
FROM orders
WHERE id IN (5, 9, 12, 14);
Result 4 rows
idstatuscustomer_message
5cancelledCancelled
9shippedOn its way
12pendingAwaiting dispatch
14deliveredComplete

When every condition compares the same expression for equality, the simple form is shorter. It can't test for NULL (WHEN NULL never matches), so use the searched form for that.

CASE in ORDER BY

SQL
SELECT id, status
FROM orders
ORDER BY CASE status
           WHEN 'pending' THEN 1
           WHEN 'shipped' THEN 2
           WHEN 'delivered' THEN 3
           ELSE 4
         END,
         id
LIMIT 7;
Result 7 rows
idstatus
12pending
13pending
15pending
9shipped
11shipped
1delivered
2delivered

A custom order that isn't alphabetical — here, the order a warehouse wants to see orders in.

CASE with NULL checks

SQL
SELECT name,
       CASE
         WHEN email IS NULL THEN 'Missing email'
         WHEN city IS NULL THEN 'Missing city'
         ELSE 'Complete'
       END AS profile
FROM customers
WHERE email IS NULL OR city IS NULL OR id = 1;
Result 3 rows
nameprofile
Ana PereiraComplete
Farah HaddadMissing city
Julia SantosMissing email

CASE inside aggregates

SQL
SELECT COUNT(*) AS orders,
       SUM(CASE WHEN status = 'delivered' THEN 1 ELSE 0 END) AS delivered,
       SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending
FROM orders;
Result 1 row
ordersdeliveredpending
1593

Counting several categories in a single pass is a powerful pattern — lesson 21 builds on it.

Shortcut functions

FunctionEquivalent toWhere
COALESCE(a, b)CASE WHEN a IS NOT NULL THEN a ELSE b ENDEverywhere
NULLIF(a, b)CASE WHEN a = b THEN NULL ELSE a ENDEverywhere
IIF(cond, a, b)CASE WHEN cond THEN a ELSE b ENDSQLite, SQL Server
IF(cond, a, b)SameMySQL
SQL
SELECT name, stock, IIF(stock = 0, 'Sold out', 'Available') AS availability
FROM products
WHERE category = 'Electronics';
Result 4 rows
namestockavailability
Wireless Earbuds60Available
USB-C Charger120Available
Mechanical Keyboard0Sold out
Smart Plug75Available

Try it yourself

Label each employee as "Junior" (under 55,000), "Mid" (under 80,000) or "Senior".

Try it yourself
Result