Post

CASE WHEN — Conditional Logic in SQL

CASE WHEN — Conditional Logic in SQL

title: “CASE WHEN (Conditional Logic)” part: 2 topic_number: 2 slug: “case-when” difficulty: “Intermediate” prerequisites: “select-from-where” —

CASE WHEN — Conditional Logic in SQL

What Is It?

CASE WHEN is SQL’s if-then-else statement. It lets you create conditional logic directly in your queries — returning different values based on conditions.

Real-world analogy: Like a decision tree — “If this condition is true, do X; otherwise, if that condition is true, do Y; else do Z.”

When you’d use it:

  • Categorize data (e.g., salary ranges into “Low”, “Medium”, “High”)
  • Create custom labels or flags
  • Conditional calculations
  • Data transformation and formatting

Syntax Breakdown

Simple CASE (compare one value)

1
2
3
4
5
6
CASE expression
    WHEN value1 THEN result1
    WHEN value2 THEN result2
    ...
    ELSE default_result
END

Searched CASE (multiple conditions)

1
2
3
4
5
6
CASE
    WHEN condition1 THEN result1
    WHEN condition2 THEN result2
    ...
    ELSE default_result
END

Key points:

  • WHEN specifies the condition
  • THEN specifies the result if condition is true
  • ELSE provides a default (optional but recommended)
  • END closes the CASE statement
  • Evaluated top-to-bottom (first match wins)

Basic Examples

Categorize Salaries

1
2
3
4
5
6
7
8
9
10
11
12
SELECT 
    first_name,
    last_name,
    salary,
    CASE
        WHEN salary >= 90000 THEN 'High'
        WHEN salary >= 70000 THEN 'Medium'
        ELSE 'Low'
    END AS salary_category
FROM employees
ORDER BY salary DESC
LIMIT 8;

Expected output:

first_name last_name salary salary_category
Sam Clark 95000.00 High
Frank Miller 93000.00 High
Henry Moore 92000.00 High
Paul Garcia 87000.00 Medium
Bob Smith 82000.00 Medium
Alice Johnson 75000.00 Medium
Ivy Anderson 74000.00 Medium
George Jones 71000.00 Medium

How it works: Checks conditions from top to bottom. First match wins.

Simple CASE (Exact Matches)

1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT 
    department_id,
    department_name,
    CASE department_name
        WHEN 'Engineering' THEN 'Tech'
        WHEN 'Data Science' THEN 'Tech'
        WHEN 'IT' THEN 'Tech'
        WHEN 'Sales' THEN 'Revenue'
        WHEN 'Marketing' THEN 'Revenue'
        ELSE 'Operations'
    END AS division
FROM departments
ORDER BY division, department_name;

Expected output:

department_id department_name division
5 Finance Operations
4 Human Resources Operations
7 Legal Operations
2 Marketing Revenue
3 Sales Revenue
8 Data Science Tech
1 Engineering Tech

Departments grouped into divisions.


Going Deeper

Conditional Calculations

Give bonuses based on salary: 10% for high earners, 15% for others:

1
2
3
4
5
6
7
8
9
10
11
SELECT 
    first_name,
    last_name,
    salary,
    CASE
        WHEN salary >= 80000 THEN salary * 0.10
        ELSE salary * 0.15
    END AS bonus
FROM employees
ORDER BY salary DESC
LIMIT 5;

Expected output:

first_name last_name salary bonus
Sam Clark 95000.00 9500.00
Frank Miller 93000.00 9300.00
Henry Moore 92000.00 9200.00
Paul Garcia 87000.00 8700.00
Bob Smith 82000.00 8200.00

Higher earners get smaller percentage, but larger absolute bonus.

Multi-Condition Logic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT 
    first_name,
    last_name,
    salary,
    hire_date,
    CASE
        WHEN salary >= 90000 AND hire_date < '2020-01-01' THEN 'Senior High Earner'
        WHEN salary >= 90000 THEN 'High Earner'
        WHEN hire_date < '2020-01-01' THEN 'Veteran'
        ELSE 'Standard'
    END AS employee_type
FROM employees
ORDER BY salary DESC, hire_date
LIMIT 10;

Expected output:

first_name last_name salary hire_date employee_type
Frank Miller 93000.00 2018-09-01 Senior High Earner
Henry Moore 92000.00 2019-04-18 Senior High Earner
Sam Clark 95000.00 2021-08-05 High Earner
Paul Garcia 87000.00 2020-10-22 Standard
Bob Smith 82000.00 2020-05-10 Standard

Complex categorization based on multiple factors.

CASE in WHERE Clause

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- Find employees: high earners in Engineering, or anyone in Sales
SELECT 
    e.first_name,
    e.last_name,
    e.salary,
    d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE 
    CASE d.department_name
        WHEN 'Engineering' THEN e.salary >= 80000
        WHEN 'Sales' THEN TRUE
        ELSE FALSE
    END
ORDER BY d.department_name, e.salary DESC;

Expected output:

first_name last_name salary department_name
Sam Clark 95000.00 Engineering
Paul Garcia 87000.00 Engineering
Bob Smith 82000.00 Sales
Carol Williams 78000.00 Sales
Noah Harris 64000.00 Sales

Different criteria for different departments.

Pause and Predict: What happens if no WHEN conditions match and there’s no ELSE clause?

Answer `NULL` If no conditions match and no ELSE is specified, CASE returns NULL. That's why ELSE clauses are recommended!

Watch Out — Common Mistakes

Mistake #1: Forgetting END

1
2
3
4
5
6
7
8
--  SYNTAX ERROR
SELECT 
    salary,
    CASE
        WHEN salary >= 80000 THEN 'High'
        ELSE 'Low'
    -- Missing END!
FROM employees;

Error: SQL syntax error near ‘FROM’

Fix: Always close with END:

1
2
3
4
5
6
7
8
-- • CORRECT
SELECT 
    salary,
    CASE
        WHEN salary >= 80000 THEN 'High'
        ELSE 'Low'
    END AS salary_level
FROM employees;

Mistake #2: Wrong Order of Conditions

1
2
3
4
5
6
--  LOGIC ERROR
CASE
    WHEN salary >= 50000 THEN 'High'   -- This matches EVERYTHING >= 50k!
    WHEN salary >= 80000 THEN 'Very High'  -- This NEVER runs
    ELSE 'Low'
END

Problem: First condition catches all salaries over 50k, so the second condition never executes.

Fix: Order from most specific to least specific:

1
2
3
4
5
6
-- • CORRECT
CASE
    WHEN salary >= 80000 THEN 'Very High'
    WHEN salary >= 50000 THEN 'High'
    ELSE 'Low'
END

Mistake #3: Mixing Data Types

1
2
3
4
5
6
--  INCONSISTENT TYPES
CASE
    WHEN salary >= 80000 THEN 'High'
    WHEN salary >= 50000 THEN 75000    -- Number instead of string!
    ELSE 'Low'
END

Problem: MySQL will try to convert everything to the same type, leading to unexpected results.

Fix: Keep all THEN results the same data type:

1
2
3
4
5
6
-- • CORRECT (all strings)
CASE
    WHEN salary >= 80000 THEN 'High'
    WHEN salary >= 50000 THEN 'Medium'
    ELSE 'Low'
END

Edge Case Spotlight

NULL Handling in Conditions

1
2
3
4
5
6
7
SELECT 
    'Test' AS label,
    CASE
        WHEN NULL = NULL THEN 'Equal'  -- Never true!
        WHEN NULL IS NULL THEN 'Is Null'  -- This works
        ELSE 'Not Null'
    END AS result;

Expected output:

label result
Test Is Null

Key: Use IS NULL, not = NULL in conditions.

Nested CASE Statements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT 
    first_name,
    salary,
    hire_date,
    CASE
        WHEN salary >= 80000 THEN
            CASE
                WHEN hire_date < '2020-01-01' THEN 'Senior Level'
                ELSE 'New High Earner'
            END
        ELSE 'Standard'
    END AS classification
FROM employees
LIMIT 5;

Expected output:

first_name salary hire_date classification
Alice 75000.00 2019-03-15 Standard
Bob 82000.00 2020-05-10 New High Earner
Carol 78000.00 2020-08-20 Standard
David 66000.00 2021-11-30 Standard
Eve 69000.00 2022-02-14 Standard

Nested CASE for complex decision trees (but can get hard to read).


Try This

Exercise 1 (Guided)

Categorize employees by tenure: ‘Veteran’ (hired before 2019), ‘Experienced’ (2019-2021), ‘Recent’ (2022 or later). Show first_name, last_name, hire_date, and tenure_category. Sort by hire_date.

Hint Use CASE WHEN with date comparisons: hire_date < '2019-01-01', etc.

Exercise 2 (Independent)

Create a “performance bonus” calculator:

  • Salary >= 90k: 5% bonus
  • Salary 70k-89k: 8% bonus
  • Salary < 70k: 10% bonus

Show first_name, last_name, salary, bonus_percentage (‘5%’, ‘8%’, ‘10%’), and bonus_amount (calculated).

Hint Use CASE for both bonus_percentage (text) and bonus_amount (calculation with salary * 0.05, etc.).

Exercise 3 (Challenge)

Create a department “health” report. For each department, calculate:

  • employee_count
  • avg_salary
  • health_status: ‘Excellent’ if avg_salary > 75k AND employee_count >= 3, ‘Good’ if avg_salary > 70k, ‘Needs Review’ otherwise

Show department_name, employee_count, avg_salary, and health_status. Sort by health_status, then avg_salary DESC.

Hint JOIN employees and departments, GROUP BY department, use CASE with AND conditions on aggregates.

Answer Key

Exercise 1 Answer ```sql SELECT first_name, last_name, hire_date, CASE WHEN hire_date < '2019-01-01' THEN 'Veteran' WHEN hire_date < '2022-01-01' THEN 'Experienced' ELSE 'Recent' END AS tenure_category FROM employees ORDER BY hire_date; ``` **Expected output:** | first_name | last_name | hire_date | tenure_category | |------------|-----------|-----------|-----------------| | George | Jones | 2017-07-12 | Veteran | | Ivy | Anderson | 2018-02-20 | Veteran | | Frank | Miller | 2018-09-01 | Veteran | | Alice | Johnson | 2019-03-15 | Experienced | | ... | ... | ... | ... | Employees categorized by hire date.
Exercise 2 Answer ```sql SELECT first_name, last_name, salary, CASE WHEN salary >= 90000 THEN '5%' WHEN salary >= 70000 THEN '8%' ELSE '10%' END AS bonus_percentage, CASE WHEN salary >= 90000 THEN salary * 0.05 WHEN salary >= 70000 THEN salary * 0.08 ELSE salary * 0.10 END AS bonus_amount FROM employees ORDER BY salary DESC; ``` **Expected output:** | first_name | last_name | salary | bonus_percentage | bonus_amount | |------------|-----------|---------|------------------|--------------| | Sam | Clark | 95000.00 | 5% | 4750.00 | | Frank | Miller | 93000.00 | 5% | 4650.00 | | Henry | Moore | 92000.00 | 5% | 4600.00 | | Paul | Garcia | 87000.00 | 8% | 6960.00 | | Bob | Smith | 82000.00 | 8% | 6560.00 | | ... | ... | ... | ... | ... | Lower salaries get higher percentage bonuses.
Exercise 3 Answer ```sql SELECT d.department_name, COUNT(e.employee_id) AS employee_count, ROUND(AVG(e.salary), 2) AS avg_salary, CASE WHEN AVG(e.salary) > 75000 AND COUNT(e.employee_id) >= 3 THEN 'Excellent' WHEN AVG(e.salary) > 70000 THEN 'Good' ELSE 'Needs Review' END AS health_status FROM departments d LEFT JOIN employees e ON d.department_id = e.department_id GROUP BY d.department_id, d.department_name ORDER BY CASE health_status WHEN 'Excellent' THEN 1 WHEN 'Good' THEN 2 ELSE 3 END, avg_salary DESC; ``` **Expected output:** | department_name | employee_count | avg_salary | health_status | |-----------------|----------------|------------|---------------| | Engineering | 3 | 85666.67 | Excellent | | Marketing | 2 | 87500.00 | Good | | Finance | 2 | 79000.00 | Good | | Sales | 3 | 69666.67 | Needs Review | | ... | ... | ... | ... | Department health assessment based on size and salaries. **Note:** The ORDER BY uses CASE to sort by health status category, then by salary within each category.

Quick Recap

CASE WHEN provides if-then-else logic in SQL
Simple CASE compares one expression to values
Searched CASE evaluates multiple conditions
• Conditions evaluated top-to-bottom (first match wins)
ELSE provides default (returns NULL if omitted)
• Must close with END
• Keep THEN results the same data type
• Use IS NULL for NULL checks, not = NULL


Up Next

Next topic: Self Joinspart2_03_self_joins.md

Type ‘next’ when ready to continue!

This post is licensed under CC BY 4.0 by the author.