CASE WHEN: More Than an If-Statement
CASE WHEN is SQL's conditional expression — most people learn it as a simple if/else for labeling rows, but it's actually one of the most versatile tools in SQL, showing up inside aggregates for conditional counting, inside ORDER BY for custom sort logic, and as the standard mechanism behind pivoting. This guide goes past the basic syntax into the patterns that come up constantly in real reporting and interview questions.
Basic Syntax: Simple and Searched CASE
-- Simple CASE: compares one expression against several possible values
SELECT name,
CASE department
WHEN 'ENG' THEN 'Engineering'
WHEN 'MKT' THEN 'Marketing'
ELSE 'Other'
END AS department_name
FROM employees;
-- Searched CASE: evaluates independent boolean conditions — far more flexible
SELECT name, salary,
CASE
WHEN salary >= 150000 THEN 'Senior'
WHEN salary >= 90000 THEN 'Mid'
ELSE 'Junior'
END AS level
FROM employees;
The searched form (CASE WHEN condition THEN ...) is used far more often in practice, since it supports arbitrary boolean logic rather than just equality against a single expression. Order matters — conditions are evaluated top to bottom, and the first match wins, so put the most specific or highest-priority condition first.
Conditional Aggregation: CASE Inside SUM/COUNT/AVG
This is arguably the single highest-value CASE WHEN pattern — turning multiple rows into multiple conditional counts or sums within one GROUP BY, without needing separate queries.
SELECT
department,
COUNT(*) AS total_employees,
SUM(CASE WHEN salary > 100000 THEN 1 ELSE 0 END) AS high_earners,
AVG(CASE WHEN gender = 'F' THEN salary END) AS avg_female_salary
FROM employees
GROUP BY department;
Note the third line: leaving off the ELSE clause means non-matching rows evaluate to NULL, which AVG() automatically ignores — so avg_female_salary correctly averages only the matching rows, not all rows in the group. This is the exact same mechanism behind manual pivoting; see our pivot and unpivot guide for the full pattern.
CASE Inside WHERE and HAVING
-- Conditional filtering logic based on another column's value
SELECT * FROM orders
WHERE
CASE
WHEN order_type = 'international' THEN shipping_cost > 50
ELSE shipping_cost > 20
END;
This pattern is less common than conditional aggregation but useful when a filter's threshold genuinely depends on another column — though for anything beyond a couple of branches, it's often clearer to just write two separate OR-connected conditions instead.
CASE Inside ORDER BY: Custom Sort Logic
-- Sort by a custom priority order that isn't alphabetical or numeric
SELECT * FROM tickets
ORDER BY
CASE priority
WHEN 'urgent' THEN 1
WHEN 'high' THEN 2
WHEN 'medium' THEN 3
WHEN 'low' THEN 4
END;
Without this, sorting the text values 'urgent', 'high', 'medium', 'low' alphabetically would produce a meaningless order — mapping each to a numeric priority inside CASE lets you sort by real business priority instead of string order.
CASE for NULL-Safe Bucketing
SELECT
CASE
WHEN age IS NULL THEN 'Unknown'
WHEN age < 18 THEN 'Minor'
WHEN age BETWEEN 18 AND 64 THEN 'Adult'
ELSE 'Senior'
END AS age_group,
COUNT(*)
FROM users
GROUP BY 1;
Handling NULL as an explicit first branch (rather than letting it fall through unexpectedly) is a good habit — without it, a NULL age would silently fall through every numeric comparison (since NULL < 18 is UNKNOWN, not TRUE) and land in the ELSE bucket, which is usually not what you want. See our NULL handling guide for the full logic behind this.
Nested CASE Expressions
SELECT
name,
CASE
WHEN department = 'Engineering' THEN
CASE WHEN salary > 150000 THEN 'Senior Engineer' ELSE 'Engineer' END
WHEN department = 'Sales' THEN
CASE WHEN salary > 120000 THEN 'Senior Sales' ELSE 'Sales' END
ELSE 'Other'
END AS title
FROM employees;
Nesting works but readability degrades fast — beyond two levels, most engineers switch to a lookup table joined in, or break the logic into a CTE with intermediate labeled columns instead of one deeply nested expression.
CASE vs. COALESCE vs. NULLIF
COALESCE and NULLIF (covered in our NULL handling guide) are technically both shorthand for specific, common CASE patterns:
-- COALESCE(a, b) is equivalent to:
CASE WHEN a IS NOT NULL THEN a ELSE b END
-- NULLIF(a, b) is equivalent to:
CASE WHEN a = b THEN NULL ELSE a END
Knowing this equivalence is a good interview signal — it shows you understand these aren't separate magic keywords, but readable shortcuts for patterns CASE could also express directly.
Common Mistakes
- Forgetting ELSE when a default is actually needed — an unmatched row silently returns NULL instead of erroring, which can be surprising downstream if not intentional.
- Wrong condition order — since the first matching WHEN wins, an overly broad condition placed too early can silently shadow more specific conditions listed after it.
- Not handling NULL explicitly in a chain of comparisons — a NULL value falls through every numeric/string comparison silently (since they all evaluate to UNKNOWN) and lands in ELSE, often unintentionally.
- Over-nesting CASE expressions instead of extracting the logic into a CTE or a joined lookup table once it gets more than 2 levels deep.
Common Interview Questions
- Write a query to count how many employees fall into each of several salary bands. Conditional aggregation with SUM(CASE WHEN ... THEN 1 ELSE 0 END), or GROUP BY on a CASE-derived bucket column.
- How would you sort rows by a custom, non-alphabetical priority order? CASE inside ORDER BY, mapping each category to a numeric rank.
- What's the relationship between CASE, COALESCE, and NULLIF? Both COALESCE and NULLIF are shorthand for specific common CASE patterns — walk through the equivalence shown above.
- Why might a CASE expression return an unexpected NULL? A missing ELSE clause when no WHEN condition matches — including the common trap of a NULL input silently failing every comparison in the chain.
Frequently Asked Questions
Does CASE WHEN evaluation order matter for performance, not just correctness?
Mostly for correctness — but some databases do short-circuit evaluation, stopping at the first matching condition, so placing the most commonly-true condition first can offer a small performance benefit on very large row counts, in addition to being important for correctness with overlapping conditions.
Can CASE WHEN be used in an UPDATE statement?
Yes — UPDATE employees SET level = CASE WHEN salary > 100000 THEN 'Senior' ELSE 'Junior' END is a common pattern for bulk-updating a derived column based on conditional logic.
Practice CASE WHEN Patterns
Conditional logic is one of the most frequently reused SQL skills across reporting, data cleaning, and analytics work. Try it hands-on with our SQL practice questions, or apply it to real business logic in our case studies. For the pivoting technique built directly on conditional aggregation, see our pivot and unpivot guide.