CASE WHEN

Add if-then-else logic directly inside your queries

Intermediate 14 minCASEconditionallogic

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:

Top-to-bottom evaluation — Conditions are checked in order. The first match wins and all remaining WHENs are skipped entirely. Structure: CASE → WHEN condition THEN result → ELSE default → END.
Always include ELSE — Without an ELSE clause, rows that don't match any WHEN condition return NULL. This can silently break downstream calculations, aggregates, and reports without any visible error.
Works inside aggregatesSUM(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 order is criticalWHEN 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.
Knowledge Canvas

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
Syntax
Syntax Template
1CASE
2 WHEN condition1 THEN result1
3 WHEN condition2 THEN result2
4 ELSE default_result
5END AS alias
WHEN ... THENIf condition is true, return this result
ELSEDefault if no conditions match (returns NULL if omitted)
ENDRequired — closes the CASE expression
AS aliasName the computed column
Sample Data
orders
12 rows
idtotalstatus
500159.98delivered
5002449delivered
500312.5shipped
500489.99delivered
5005245pending
5006159delivered
500745.5shipped
500879cancelled
5009425delivered
501018.99pending
5011134delivered
501265.5cancelled
products
10 rows
idnamecategorypricestock
P101Wireless MouseElectronics24.99150
P102Office DeskFurniture18925
P103LED Desk LampFurniture45.580
P104USB-C CableElectronics12.99500
P105Bluetooth SpeakerElectronics7960
P106Office ChairFurniture9515
P107Coffee MakerAppliances89.9940
P108Notebook SetStationery8.5200
P109Wireless HeadphonesElectronics15930
P110Standing DeskFurniture4258

Worked Example

Categorize orders as "High" (above 200), "Medium" (50-200), or "Low" (below 50).

SQL
1SELECT
2 id,
3 total,
4 CASE
5 WHEN total > 200 THEN 'High'
6 WHEN total >= 50 THEN 'Medium'
7 ELSE 'Low'
8 END AS value_tier
9FROM orders
10ORDER BY
11 value_tier,
12 total DESC
13LIMIT 6;
For each row, CASE evaluates conditions top to bottom. Order #5007 (748) hits total > 200 first → "High". An order of 59.98 fails the first WHEN but hits total >= 50 → "Medium". An order of 12.50 fails both → ELSE → "Low". The order of WHEN clauses matters! We ORDER BY value_tier then total DESC so the preview shows a sample from each tier.
Output
6 rows
idtotalvalue_tier
5002449High
5009425High
5005245High
500745.5Low
501018.99Low
500312.5Low
Common Mistakes

WHEN order matters

SQL
1SELECT
2 id,
3 total,
4 CASE
5 WHEN total >= 50 THEN 'Medium'
6 WHEN total > 200 THEN 'High'
7 ELSE 'Low'
8 END AS value_tier
9FROM orders
10ORDER BY total DESC
11LIMIT 3;

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

1First matching WHEN wins — put the most specific condition first
2CASE without ELSE returns NULL for unmatched rows
3CASE works inside SUM, COUNT, and other aggregates
4Use CASE in ORDER BY for custom sort logic

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.

Challenge

Solve the problem below

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.

Your Query