SQL CASE WHEN: write conditions in a query
“If the price is above €100, show premium, otherwise standard.” For this kind of logic, SQL offers the CASE WHEN expression, the equivalent of “if… then… else” in programming languages. You can use it anywhere a value is expected: in the SELECT, the ORDER BY, and even inside a SUM.
Syntax
CASE
WHEN condition1 THEN value1
WHEN condition2 THEN value2
ELSE default_value
ENDHow it works
SQL tests the WHEN conditions in order and returns the value of the first true condition. If none is true, it returns the ELSE value, or NULL if there is no ELSE. The word END closes the expression; it is often given an alias with AS.
A short form
To compare a single column with several values: CASE statut WHEN 'livrée' THEN 'OK' WHEN 'annulée' THEN 'KO' ELSE '…' END.
Counting based on a condition
SUM(CASE WHEN condition THEN 1 ELSE 0 END) counts the rows that meet a condition. With several expressions of this kind, you get a cross-tab in a single query.
Diagram
| nom | prix |
|---|---|
| Écran | 289 |
| Plaid | 25 |
| SSD | 149 |
| nom | gamme |
|---|---|
| Écran | premium |
| Plaid | standard |
| SSD | premium |
Simplified diagram to show the idea. The examples below use the real SQL Quest practice database (a fictional online shop: see the full schema).
Glossary: the sample database uses French names
clients- customers
commandes- orders
lignes_commande- order lines
produits- products
fournisseurs- suppliers
employes- employees
prenom / nom- first name / last name (or name)
ville- city
pays- country
prix- price
prix_unitaire- unit price
quantite- quantity
categorie- category
date_commande- order date
poste- job title
departement- department
salaire- salary
statut- status: livrée = delivered, expédiée = shipped, en cours = in progress, annulée = cancelled
Examples with their results
1. Create categories
A price range computed for each product.
SELECT nom, prix,
CASE
WHEN prix >= 100 THEN 'premium'
WHEN prix >= 30 THEN 'mid-range'
ELSE 'budget'
END AS price_range
FROM produits
ORDER BY prix DESC
LIMIT 8; Try it in the sandbox| nom | prix | price_range |
|---|---|---|
| Écran 27" | 342 | premium |
| SSD 1To | 317 | premium |
| Souris sans fil | 103 | premium |
| Webcam HD | 101 | premium |
| Casque gaming | 98 | mid-range |
| Bougie parfumée | 60.92 | mid-range |
| Lampe LED | 51.74 | mid-range |
| Hub USB-C | 49 | mid-range |
8 rows in total
2. Group on a computed category
The number of customers per age group.
SELECT
CASE
WHEN age IS NULL THEN 'unknown'
WHEN age < 30 THEN 'under 30'
WHEN age < 50 THEN '30 to 49'
ELSE '50 and over'
END AS age_group,
COUNT(*) AS nb
FROM clients
GROUP BY age_group
ORDER BY nb DESC; Try it in the sandbox| age_group | nb |
|---|---|
| 50 and over | 50 |
| 30 to 49 | 47 |
| under 30 | 19 |
| unknown | 4 |
4 rows in total
3. Count based on a condition
For each year, the total number of orders and the number of cancelled orders.
SELECT strftime('%Y', date_commande) AS annee,
COUNT(*) AS total,
SUM(CASE WHEN statut = 'annulée' THEN 1 ELSE 0 END) AS annulees
FROM commandes
GROUP BY annee; Try it in the sandbox| annee | total | annulees |
|---|---|---|
| 2023 | 190 | 39 |
| 2024 | 214 | 28 |
| 2025 | 196 | 25 |
3 rows in total
Common mistakes
Forgetting END
Every CASE must end with END, otherwise the query causes a syntax error.
SELECT nom, CASE WHEN prix > 100 THEN 'expensive' ELSE 'affordable' AS price_range FROM produits;SELECT nom, CASE WHEN prix > 100 THEN 'expensive' ELSE 'affordable' END AS price_range FROM produits;Putting the conditions in the wrong order
The first true condition wins: here, a €300 price is classified “mid-range” because prix >= 30 is tested first.
SELECT nom, CASE WHEN prix >= 30 THEN 'mid-range' WHEN prix >= 100 THEN 'premium' END FROM produits;SELECT nom, CASE WHEN prix >= 100 THEN 'premium' WHEN prix >= 30 THEN 'mid-range' END FROM produits;Forgetting the ELSE
Without ELSE, the rows that meet no condition get NULL, which often skews calculations.
SELECT SUM(CASE WHEN statut = 'livrée' THEN 1 END) FROM commandes;SELECT SUM(CASE WHEN statut = 'livrée' THEN 1 ELSE 0 END) FROM commandes;Exercises
Try writing the query yourself before opening the solution.
Challenge 1. Show each product with an “availability” column: “out of stock” if the stock is 0, “low” below 20, otherwise “ok”.
Show a solution
SELECT nom, stock, CASE WHEN stock = 0 THEN 'out of stock' WHEN stock < 20 THEN 'low' ELSE 'ok' END AS availability FROM produits;| nom | stock | availability |
|---|---|---|
| Clavier mécanique | 105 | ok |
| Souris sans fil | 75 | ok |
| Écran 27" | 102 | ok |
| Webcam HD | 21 | ok |
| SSD 1To | 90 | ok |
| Casque gaming | 37 | ok |
| Hub USB-C | 162 | ok |
| Lampe LED | 15 | low |
29 rows in total · 8 shown
Try it in the sandboxChallenge 2. For each city, count the customers under 30 and those aged 30 and over.
Show a solution
SELECT ville, SUM(CASE WHEN age < 30 THEN 1 ELSE 0 END) AS under_30, SUM(CASE WHEN age >= 30 THEN 1 ELSE 0 END) AS others FROM clients GROUP BY ville;| ville | under_30 | others |
|---|---|---|
| Bordeaux | 4 | 9 |
| Bruxelles | 0 | 8 |
| Casablanca | 2 | 8 |
| Genève | 0 | 5 |
| Lille | 3 | 6 |
| Lyon | 1 | 11 |
| Marseille | 1 | 9 |
| Montréal | 3 | 7 |
12 rows in total · 8 shown
Try it in the sandboxThe interactive exercises are checked automatically, with a tutor who explains your mistakes. A short lesson stays next to the editor.
Frequently asked questions
Can I use CASE in a WHERE clause?
Yes, but it is rarely needed: a combination of AND and OR is usually easier to read.
Is there an IF function in SQL?
MySQL has IF(condition, a, b) and SQLite has IIF(condition, a, b) since version 3.32. CASE WHEN is the standard version that works everywhere.
Do the THEN values have to be of the same type?
It is strongly recommended. Mixing text and numbers works in SQLite, but causes errors or conversions in other databases.