SQL Server CASE Statement

The SQL Server CASE statement evaluates a list of conditions and returns one of multiple possible result expressions.

Although it’s commonly known as the “CASE statement”, the SQL Server documentation lists it as an expression. Plus it’s referred to as the “CASE expression” in the SQL standard. Some other DBMSs distinguish between the CASE statement and the CASE expression. Either way, here’s a quick rundown on how the “CASE statement” (or CASE expression) works in SQL Server.

Syntax

There are two formats of CASE expression:

  • Simple CASE expression
  • Searched CASE expression

Here’s the syntax for the simple CASE expression:

CASE input_expression   
     WHEN when_expression THEN result_expression [ ...n ]   
     [ ELSE else_result_expression ]   
END

Here’s the syntax for the searched CASE expression:

CASE  
     WHEN Boolean_expression THEN result_expression [ ...n ]   
     [ ELSE else_result_expression ]   
END

The return type is the highest precedence type from the set of types in result_expressions and the optional else_result_expression.

Below are examples of each.

The Simple CASE Expression

The simple CASE expression compares an expression to a set of simple expressions to determine the result

Example:

DECLARE @stock_ticker varchar(4) = 'V';

SELECT Company =  
    CASE @stock_ticker  
        WHEN 'AAPL' THEN 'Apple'
        WHEN 'FB' THEN 'Meta'
        WHEN 'V' THEN 'Visa'
        ELSE 'Not in the portfolio'  
    END

Result:

+-----------+
| Company   |
|-----------|
| Visa      |
+-----------+

In this example, my CASE expression is part of a SELECT statement. It checks for three conditions and has an ELSE to cater for anything that isn’t covered in the three conditions.

In this case, the stock ticker V matches the third WHEN expression, and the expression provided by THEN is returned.

To be clear, the actual CASE expression is this part:

    CASE @stock_ticker  
        WHEN 'AAPL' THEN 'Apple'
        WHEN 'FB' THEN 'Meta'
        WHEN 'MA' THEN 'Mastercard'
        WHEN 'V' THEN 'Visa'
        ELSE 'Not in the portfolio'  
    END

What CASE does is check the value of each WHEN expression against the input expression. In my example, the @stock_ticker variable is the input expression. Therefore, it’s checking the value of each WHEN expression against the @stock_ticker variable.

When/if it finds a match, it returns the expression provided by THEN.

My example uses three WHEN expressions, but it could have been more and it could have been less, depending on the requirements.

The Searched CASE Expression

The searched CASE expression evaluates a set of Boolean expressions to determine the result.

Here’s an example of a searched CASE expression.

DECLARE @price int = 1500;

SELECT Affordability =  
    CASE   
        WHEN @price < 100 THEN 'Cheap'
        WHEN @price >= 100 AND @price < 500 THEN 'Affordable'
        ELSE 'Expensive'  
    END

Result:

+-----------------+
| Affordability   |
|-----------------|
| Expensive       |
+-----------------+

A searched CASE expression doesn’t have an input expression like the simple CASE expression.

You’ll recall that in our simple CASE expression, it started off with CASE @stock_ticker, and therefore we knew that the WHEN expressions were all evaluating against the value of @stock_ticker.

With the searched CASE expression, we don’t provide an input expression at the start like that. Instead, each WHEN expression includes a Boolean expression for which to be evaluated against.

A Database Example

Here’s an example that demonstrates how the CASE expression can be used within a database query.

USE WideWorldImporters;
SELECT 
    CityName AS [City], 
    LatestRecordedPopulation AS [Population], 
    Size =  
      CASE 
         WHEN LatestRecordedPopulation < 2000000 THEN 'Small City'  
         WHEN LatestRecordedPopulation >= 2000000 AND LatestRecordedPopulation < 3000000 THEN 'Big City' 
         ELSE 'Really Big City'
      END 
FROM Application.Cities
WHERE LatestRecordedPopulation > 1000000;

Result:

+--------------+--------------+-----------------+
| City         | Population   | Size            |
|--------------+--------------+-----------------|
| Brooklyn     | 2565635      | Big City        |
| Chicago      | 2695598      | Big City        |
| Dallas       | 1197816      | Small City      |
| Houston      | 2099451      | Big City        |
| Los Angeles  | 3792621      | Really Big City |
| Manhattan    | 1619090      | Small City      |
| New York     | 8175133      | Really Big City |
| Philadelphia | 1526006      | Small City      |
| Phoenix      | 1445632      | Small City      |
| Queens       | 2272771      | Big City        |
| San Antonio  | 1327407      | Small City      |
| San Diego    | 1307402      | Small City      |
| The Bronx    | 1408473      | Small City      |
+--------------+--------------+-----------------+

This example uses a searched CASE expression to evaluate the results from the LatestRecordedPopulation column of the Application.Cities table.

Data Types

In SQL Server, the data type of the input expression and the WHEN expressions must be the same or must be an implicit conversion.

Here’s what happens if they aren’t:

DECLARE @stock_ticker varchar(4) = 'V';

SELECT Company =  
      CASE @stock_ticker  
         WHEN 1 THEN 'Apple'
         WHEN 2 THEN 'Meta'
         WHEN 3 THEN 'Mastercard'
         WHEN 4 THEN 'Visa'
         ELSE 'Not in the portfolio'  
      END

Result:

Msg 245, Level 16, State 1, Line 3
Conversion failed when converting the varchar value 'V' to data type int.

Order of Evaluation

The T-SQL CASE expression evaluates its conditions sequentially and stops with the first condition whose condition is satisfied.

To demonstrate this, let’s use multiple WHEN expressions that share the same value:

DECLARE @stock_ticker varchar(4) = 'V';

SELECT Company =  
    CASE @stock_ticker  
        WHEN 'V' THEN 'Visa 1'
        WHEN 'V' THEN 'Visa 2'
        WHEN 'V' THEN 'Visa 3'
        ELSE 'Not in the portfolio'  
    END

Result:

+-----------+
| Company   |
|-----------|
| Visa 1    |
+-----------+

In this case, it stopped at the first WHEN expression.

There may be the occasional scenario where an expression is evaluated before a CASE expression receives the results of the expression as its input. In such scenarios, you could end up with an error. This could happen if you include an aggregate expression as the WHEN expression.

For this reason, Microsoft advises that:

You should only depend on order of evaluation of the WHEN conditions for scalar expressions (including non-correlated sub-queries that return scalars), not for aggregate expressions.

ELSE is Optional

The ELSE argument is optional. Therefore, we could rewrite our “affordability” example as follows:

DECLARE @price int = 1500;

SELECT Affordability =  
    CASE   
        WHEN @price < 100 THEN 'Cheap'
        WHEN @price >= 100 AND @price < 500 THEN 'Affordable'
        WHEN @price >= 500 THEN 'Expensive'
    END    

Result:

+-----------------+
| Affordability   |
|-----------------|
| Expensive       |
+-----------------+

Although, be mindful that you could end up with NULL if you omit the ELSE argument.

The following example results in NULL:

DECLARE @price int = 1500;

SELECT Affordability =  
    CASE   
        WHEN @price < 100 THEN 'Cheap'
        WHEN @price >= 100 AND @price < 500 THEN 'Affordable'
        WHEN @price >= 500 AND @price < 1000 THEN 'Expensive'
    END    

Result:

+-----------------+
| Affordability   |
|-----------------|
| NULL            |
+-----------------+

Nested CASE Expressions

You can nest CASE expressions if required:

DECLARE @price int, @on_sale bit;
SET @price = 1500;
SET @on_sale = 1;

SELECT Affordability =  
    CASE   
        WHEN @price < 100 THEN 'Cheap'
        WHEN @price >= 100 THEN 
            CASE @on_sale
                WHEN 0 THEN 'Expensive (but it''s not currently on sale)' 
                WHEN 1 THEN 'Expensive (and it''s already on sale!)'
            END
    END

Result:

+---------------------------------------+
| Affordability                         |
|---------------------------------------|
| Expensive (and it's already on sale!) |
+---------------------------------------+

However, it’s important to note that only 10 levels of nesting is allowed for CASE expressions in SQL Server. If you try to nest more than 10 levels, then you’ll get an error.

CASE in an ORDER BY Clause

In SQL Server, the CASE expression can be used in any statement or clause that allows a valid expression. Therefore, you can use it in statements such as SELECT, UPDATE, DELETE and SET, and in clauses such as IN, WHERE, ORDER BY, GROUP BY, and HAVING.

Using a CASE expression in a statement’s ORDER BY clause can be handy when you want to make a special exception for certain values when ordering your results.

Suppose we run the following query against a table containing music genres.

SELECT Genre 
FROM MusicGenres
ORDER BY Genre ASC;

Result:

+---------+
| Genre   |
|---------|
| Blues   |
| Country |
| Hip Hop |
| Jazz    |
| Metal   |
| Other   |
| Pop     |
| Rap     |
| Rock    |
+---------+

Here, we order the results by the Genre column, in ascending order.

This is fine except for one thing. The genre called Other. Wouldn’t it be nice if we could move Other to the bottom?

We can achieve this with the CASE expression by taking the above query, and modifying it as follows.

SELECT Genre
FROM MusicGenres
ORDER BY 
    CASE Genre
        WHEN 'Other' THEN 1
        ELSE 0
    END
    ASC, Genre ASC;

Result:

+---------+
| Genre   |
|---------|
| Blues   |
| Country |
| Hip Hop |
| Jazz    |
| Metal   |
| Pop     |
| Rap     |
| Rock    |
| Other   |
+---------+

CASE in an UPDATE Statement

Suppose we have the following table:

+---------+-----------+-----------+----------+
| DogId   | DogName   | GoodDog   | Dinner   |
|---------+-----------+-----------+----------|
| 1       | Fetch     | 1         | NULL     |
| 2       | Fluffy    | 0         | NULL     |
| 3       | Wag       | 0         | NULL     |
| 1001    | Brian     | 1         | NULL     |
| 1002    | Rambo     | 0         | NULL     |
| 1003    | BamBam    | 1         | NULL     |
+---------+-----------+-----------+----------+

And suppose we had recently added the Dinner column, and it’s still NULL, waiting for values to be inserted.

But the values to be inserted will depend on the value of the GoodDog column.

We could use a CASE expression in such a scenario.

UPDATE Dogs 
SET Dinner = 
    CASE GoodDog
        WHEN 1 THEN 'Sunday Roast'
        ELSE 'Airline food'
    END

SELECT * FROM Dogs;

Result:

+---------+-----------+-----------+--------------+
| DogId   | DogName   | GoodDog   | Dinner       |
|---------+-----------+-----------+--------------|
| 1       | Fetch     | 1         | Sunday Roast |
| 2       | Fluffy    | 0         | Airline food |
| 3       | Wag       | 0         | Airline food |
| 1001    | Brian     | 1         | Sunday Roast |
| 1002    | Rambo     | 0         | Airline food |
| 1003    | BamBam    | 1         | Sunday Roast |
+---------+-----------+-----------+--------------+

CASE in an INSERT Statement

We can take the table from the above example, and insert a new value.

And we can again take advantage of the CASE expression to insert the appropriate value into the Dinner column.

DECLARE @DogName nvarchar(60), @GoodDog bit;
SET @DogName = 'Lazy';
SET @GoodDog = 0;

INSERT INTO Dogs ( DogName, GoodDog, Dinner )
VALUES (
    @DogName,
    @GoodDog,
    CASE @GoodDog
        WHEN 1 THEN 'Sunday Roast'
        ELSE 'Airline food'
    END
    );

SELECT * FROM Dogs;

Result:

+---------+-----------+-----------+--------------+
| DogId   | DogName   | GoodDog   | Dinner       |
|---------+-----------+-----------+--------------|
| 1       | Fetch     | 1         | Sunday Roast |
| 2       | Fluffy    | 0         | Airline food |
| 3       | Wag       | 0         | Airline food |
| 1001    | Brian     | 1         | Sunday Roast |
| 1002    | Rambo     | 0         | Airline food |
| 1003    | BamBam    | 1         | Sunday Roast |
| 1004    | Lazy      | 0         | Airline food |
+---------+-----------+-----------+--------------+

This time the CASE expression was evaluating the value of a variable we’d just set, then inserting the appropriate value into the Dinner column.

Having one column being dependent on the value of another column is a good scenario for setting up a computed column.