CASE WHEN
Add if-then-else logic directly inside your queries
Conditional Expressions
CASE WHEN is SQL's if-then-else. It creates new computed values based on conditions, right inside your query — no need to modify the underlying data. Here's how it works:
SUM(CASE WHEN status = 'delivered' THEN total ELSE 0 END) creates conditional totals in a single query. This is a powerful pivot-style pattern for building reports without restructuring your data.WHEN total >= 50 before WHEN total > 200 means a 500 order matches the first condition and gets mislabeled. Always put the most restrictive condition first to ensure correct categorization.How CASE Works
SQL's if-then-else
- CASE evaluates conditions top to bottom — first match wins
- ELSE catches everything not matched above
- Without ELSE, unmatched rows get NULL
- Can be used in SELECT, WHERE, ORDER BY, GROUP BY
CASE Patterns
Powerful categorization techniques
- Bucketing:
CASE WHEN price < 50 THEN 'Budget' WHEN price < 200 THEN 'Mid' ELSE 'Premium' END - Conditional aggregation:
SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) - Dynamic sorting:
ORDER BY CASE WHEN priority = 'high' THEN 1 ... END - Pivoting rows to columns with CASE inside aggregates
CASE Gotchas
Watch the order
- First match wins — order conditions from specific to general
- Forgetting ELSE → NULL for unmatched rows
- CASE cannot be used for control flow (no GOTO, no loops)
- Nested CASE is possible but hard to read — prefer COALESCE
When to Use CASE
Classification and transformation
- Label numeric codes: 1→Active, 2→Paused, 3→Cancelled
- Create age/price/date buckets for reporting
- Conditional aggregation in pivot queries
- Custom sort orders not based on column values
WHEN ... THENIf condition is true, return this resultELSEDefault if no conditions match (returns NULL if omitted)ENDRequired — closes the CASE expressionAS aliasName the computed column| id | total | status |
|---|---|---|
| 5001 | 59.98 | delivered |
| 5002 | 449 | delivered |
| 5003 | 12.5 | shipped |
| 5004 | 89.99 | delivered |
| 5005 | 245 | pending |
| 5006 | 159 | delivered |
| 5007 | 45.5 | shipped |
| 5008 | 79 | cancelled |
| 5009 | 425 | delivered |
| 5010 | 18.99 | pending |
| 5011 | 134 | delivered |
| 5012 | 65.5 | cancelled |
| id | name | category | price | stock |
|---|---|---|---|---|
| P101 | Wireless Mouse | Electronics | 24.99 | 150 |
| P102 | Office Desk | Furniture | 189 | 25 |
| P103 | LED Desk Lamp | Furniture | 45.5 | 80 |
| P104 | USB-C Cable | Electronics | 12.99 | 500 |
| P105 | Bluetooth Speaker | Electronics | 79 | 60 |
| P106 | Office Chair | Furniture | 95 | 15 |
| P107 | Coffee Maker | Appliances | 89.99 | 40 |
| P108 | Notebook Set | Stationery | 8.5 | 200 |
| P109 | Wireless Headphones | Electronics | 159 | 30 |
| P110 | Standing Desk | Furniture | 425 | 8 |
Worked Example
Categorize orders as "High" (above 200), "Medium" (50-200), or "Low" (below 50).
| id | total | value_tier |
|---|---|---|
| 5002 | 449 | High |
| 5009 | 425 | High |
| 5005 | 245 | High |
| 5007 | 45.5 | Low |
| 5010 | 18.99 | Low |
| 5003 | 12.5 | Low |
WHEN order matters
A 449 order matches total >= 50 first, so it's labeled 'Medium' instead of 'High'. Run it — every order ≥ 200 also gets 'Medium' because the second WHEN is never reached.
Put the most restrictive condition first: check > 200 before >= 50.
Forgetting END
CASE without END causes a syntax error. Every CASE must be closed with END.
Always write END, optionally followed by AS alias_name.
Key Concepts
Pro Tip
Raw data rarely has the categories you need for analysis. CASE WHEN transforms and categorizes data on the fly.
When to Use
Labeling orders as high/medium/low, customer segments, price buckets, conditional aggregates, pivot-style reports.
Show product name, price, and a column called 'price_range' that says 'Budget' for under 25, 'Mid-range' for 25-100, and 'Premium' for over 100.