CASE is SQL’s “if… then…”. It evaluates a list of conditions in order and returns the value of the first one that is true. It isn’t a statement but an expression, so it can go anywhere a value can: in SELECT, ORDER BY, WHERE or inside an aggregate function.
CASE
WHEN condition1 THEN value1
WHEN condition2 THEN value2
...
ELSE defaultValue
END
Three rules worth remembering:
- Conditions are evaluated in order, and evaluation stops at the first true one.
- If none is true and there is no
ELSE, the result isNULL. - Every branch must return the same data type (or compatible types).
Searched CASE
This is the most common form: each WHEN has its own condition. The example labels each order by its total amount:
| product | totalPrice | orderSize |
|---|---|---|
| Xiaomi Mi 11 | 4304.25 | Large |
| Echo DOT 4 | 2999.50 | Medium |
| MacBook Pro M1 | 2449.50 | Medium |
| Play Station 5 | 2199.80 | Medium |
| Echo DOT 3 | 1999.50 | Medium |
| Balay Oven | 1399.10 | Medium |
| Xbox series X | 999.98 | Small |
| Nintendo Switch | 899.97 | Small |
Note that a $4,304.25 order meets the first two conditions but gets 'Large' because that one is evaluated first. If you swapped the WHEN lines, every order above $1,000 would come out as 'Medium'.
Simple CASE
When every condition compares the same column with specific values, there is a shorter form:
| product | productDescription | family |
|---|---|---|
| Xiaomi Mi 11 | Smartphone | Mobile |
| Play Station 5 | Sony Game Console | Leisure and other |
| Xbox series X | Xbox Game Console | Leisure and other |
| MacBook Pro M1 | Apple Notebook | Computers |
| Echo DOT 3 | Alexa Speaker | Smart home |
| Echo DOT 4 | Alexa Speaker | Smart home |
| Nintendo Switch | Nintendo Game Console | Leisure and other |
| Balay Oven | Appliance | Leisure and other |
Simple CASE only supports equality. For ranges (>, BETWEEN) or conditions on several columns, use the searched form.
CASE and NULL
A comparison with NULL is never true, so CASE bankAccount WHEN NULL THEN ... doesn’t work. Use the searched form with IS NULL:
| name | bankAccount | accountStatus |
|---|---|---|
| Janice | 111222333 | Account on file |
| Mavin | 123456789 | Account on file |
| Peter | 998344567 | Account on file |
| Helen | 447824556 | Account on file |
| Kimberly | 778345112 | Account on file |
| Jessie | 790763467 | Account on file |
| Paul | NULL | No account on file |
If all you need is to replace a NULL with a value, COALESCE is shorter.
CASE inside an aggregate function
This is the use that saves the most time: counting or adding up only the rows that meet a condition, for several conditions in a single query. For example, separating game console sales from everything else:
| orders | consoleOrders | consoleSales | totalSales |
|---|---|---|---|
| 8 | 3 | 4099.75 | 17251.60 |
This technique, sometimes called conditional aggregation, is the basis for turning rows into columns without PIVOT.
CASE in ORDER BY
It also lets you sort with your own criteria. Here, customers without an account go last and the rest are sorted alphabetically:
| name | lastName | bankAccount |
|---|---|---|
| Helen | Ward | 447824556 |
| Janice | Haynes | 111222333 |
| Jessie | Good | 790763467 |
| Kimberly | Lee | 778345112 |
| Mavin | Pettitt | 123456789 |
| Peter | Davis | 998344567 |
| Paul | Williams | NULL |
CASE vs IIF
SQL Server also has IIF(condition, valueIfTrue, valueIfFalse), a shortcut for a CASE with a single condition. CASE is standard SQL and works in every database; IIF only works in SQL Server (and Access). If your code needs to be portable, use CASE.
-- SQL Server only
SELECT product, IIF(totalPrice >= 1000, 'Expensive', 'Cheap') AS priceRange
FROM Orders;
Practice
- Add an
'Extra large'label for orders over $4,000. Where does theWHENhave to go for it to work, and why? - In a single query, count how many 2022 orders were placed before and after August 1.
- Sort the orders so that the ones without a customer come first.