The SQL Server CASE
statement is a powerful tool that allows you to implement conditional logic within your SQL queries. Think of it as SQL’s equivalent to an IF-THEN-ELSE
construct found in many programming languages. It enables you to evaluate different conditions and return different values based on those conditions, all within a single SQL statement. This capability is incredibly useful for data transformation, categorization, and creating more dynamic and insightful query results.
This article will delve into the intricacies of the CASE
statement in SQL Server, covering its syntax, different formats, return types, and practical examples to illustrate its versatility. Whether you are a database administrator, a data analyst, or a developer working with SQL Server, understanding the CASE
statement is essential for writing efficient and effective SQL queries.
Understanding the Two Formats of SQL Server CASE
The CASE
statement in SQL Server comes in two primary formats, each suited for different scenarios:
-
Simple CASE Expression: This format compares a single input expression against a series of simple expressions. It checks for equality between the input expression and each
WHEN
expression. -
Searched CASE Expression: This format evaluates a series of boolean expressions. It checks if each
WHEN
boolean expression evaluates to TRUE.
Both formats offer the flexibility to include an optional ELSE
clause. If none of the WHEN
conditions are met and an ELSE
clause is provided, the CASE
statement will return the value specified in the ELSE
clause. If no ELSE
clause is present and no conditions are met, the CASE
statement will return NULL
.
Let’s examine the syntax for both formats:
Simple CASE Syntax
CASE input_expression
WHEN when_expression THEN result_expression [ ...n ]
[ ELSE else_result_expression ]
END
Searched CASE Syntax
CASE
WHEN Boolean_expression THEN result_expression [ ...n ]
[ ELSE else_result_expression ]
END
Dissecting the Arguments of the CASE Statement
To effectively use the CASE
statement, it’s crucial to understand its arguments:
-
input_expression
: (Simple CASE only) This is the expression that is evaluated and compared against thewhen_expression
in eachWHEN
clause. It can be any valid SQL Server expression. -
WHEN when_expression
: (Simple CASE only) This is the expression to which theinput_expression
is compared for equality. It must have a data type compatible with or implicitly convertible to theinput_expression
‘s data type. -
THEN result_expression
: This expression specifies the value to be returned if the precedingWHEN
condition is met (eitherinput_expression
equalswhen_expression
in simple CASE, orBoolean_expression
is TRUE in searched CASE). It can be any valid SQL Server expression. -
ELSE else_result_expression
: This optional expression defines the value to be returned if none of the precedingWHEN
conditions are met. If omitted, and noWHEN
condition is true, theCASE
statement returnsNULL
. The data type must be compatible with or implicitly convertible to the data types of the otherresult_expression
s. -
WHEN Boolean_expression
: (Searched CASE only) This is a boolean expression that is evaluated. If it evaluates to TRUE, the correspondingresult_expression
is returned.
Return Types and Values of SQL Server CASE
The CASE
statement returns a value whose data type is determined by the data type precedence of the result_expression
s and the else_result_expression
. SQL Server will choose the data type with the highest precedence among all possible return expressions. For instance, if one result_expression
is an integer and another is a string, the return type might be implicitly converted to accommodate both, or the one with higher precedence will be chosen. For detailed information, refer to Data Type Precedence (Transact-SQL).
Return Values Behavior:
-
Simple CASE: It performs equality checks (
input_expression = when_expression
). It evaluates eachWHEN
clause in the order specified. Theresult_expression
of the firstWHEN
clause where the condition is TRUE is returned. If no condition is TRUE, it returnselse_result_expression
(ifELSE
is present) orNULL
(ifELSE
is absent). -
Searched CASE: It evaluates
Boolean_expression
s in the order specified. It returns theresult_expression
of the firstWHEN
clause where theBoolean_expression
is TRUE. If no condition is TRUE, it returnselse_result_expression
(ifELSE
is present) orNULL
(ifELSE
is absent).
Important Considerations and Limitations of CASE
While CASE
is incredibly useful, it’s important to be aware of certain considerations:
-
Nesting Limit: SQL Server imposes a limit of 10 levels of nesting for
CASE
expressions. While this is usually sufficient, complex logic might require restructuring queries. -
Not for Control Flow: The
CASE
expression is designed for conditional value selection within expressions. It cannot control the flow of execution in Transact-SQL statements, stored procedures, functions, or statement blocks. For control-of-flow mechanisms, use constructs likeIF...ELSE
,WHILE
, etc., as described in Control-of-Flow Language (Transact-SQL). -
Sequential Evaluation:
CASE
evaluates conditions sequentially and stops at the first satisfied condition. This is important to consider if conditions might overlap. -
Aggregate Expression Evaluation: Be cautious with aggregate expressions in
WHEN
arguments. They are evaluated before theCASE
expression, potentially leading to unexpected errors, such as divide-by-zero errors, even if theCASE
logic is intended to prevent them. Only rely on the evaluation order ofWHEN
conditions for scalar expressions. -
NULL Result Expressions: Ensure at least one
result_expression
orelse_result_expression
is not explicitly theNULL
constant. While returningNULL
is valid, having all result expressions asNULL
constants will result in an error (error 8133).
Practical Examples of SQL Server CASE Statement Usage
Let’s explore various examples to demonstrate the practical applications of the CASE
statement in different SQL Server contexts.
Example 1: Simple CASE in a SELECT Statement
This example demonstrates a simple CASE
expression within a SELECT
statement to categorize product lines. It enhances readability by replacing cryptic codes with descriptive categories.
USE AdventureWorks2022;
GO
SELECT
ProductNumber,
Category = CASE ProductLine
WHEN 'R' THEN 'Road'
WHEN 'M' THEN 'Mountain'
WHEN 'T' THEN 'Touring'
WHEN 'S' THEN 'Other sale items'
ELSE 'Not for sale'
END,
Name
FROM
Production.Product
ORDER BY
ProductNumber;
GO
Alt text: SQL query showing product numbers and categorized product lines using a CASE statement, resulting in a table with ‘Road’, ‘Mountain’, ‘Touring’, ‘Other sale items’, and ‘Not for sale’ categories.
In this query, the CASE
statement examines the ProductLine
column. Based on its value, it assigns a more user-friendly category name. The ELSE
clause handles cases where ProductLine
doesn’t match any of the specified WHEN
values, assigning ‘Not for sale’.
Example 2: Searched CASE for Price Range Categorization
This example uses a searched CASE
expression to categorize products based on their ListPrice
, displaying price ranges as text comments.
USE AdventureWorks2022;
GO
SELECT
ProductNumber,
Name,
"Price Range" = CASE
WHEN ListPrice = 0 THEN 'Mfg item - not for resale'
WHEN ListPrice BETWEEN 1 AND 50 THEN 'Budget'
WHEN ListPrice BETWEEN 51 AND 250 THEN 'Moderate'
WHEN ListPrice BETWEEN 251 AND 1000 THEN 'Expensive'
ELSE 'Luxury'
END
FROM
Production.Product
ORDER BY
ProductNumber;
GO
Alt text: SQL query demonstrating price range categorization using a searched CASE statement, outputting product number, name, and price range labels like ‘Mfg item – not for resale’, ‘Budget’, ‘Moderate’, ‘Expensive’, and ‘Luxury’.
Here, the searched CASE
expression evaluates different conditions based on ListPrice
ranges using boolean expressions (BETWEEN
operator). This allows for more complex condition checks than the simple CASE
format.
Example 3: CASE in ORDER BY Clause for Dynamic Sorting
CASE
can be effectively used within the ORDER BY
clause to achieve dynamic sorting based on column values.
SELECT
BusinessEntityID,
SalariedFlag
FROM
HumanResources.Employee
ORDER BY
CASE SalariedFlag
WHEN 1 THEN BusinessEntityID
END DESC, -- Sort salaried employees by BusinessEntityID descending
CASE
WHEN SalariedFlag = 0 THEN BusinessEntityID
END ASC; -- Sort non-salaried employees by BusinessEntityID ascending
GO
Alt text: SQL query showcasing dynamic sorting using CASE in the ORDER BY clause to sort employees differently based on their SalariedFlag status, with salaried employees sorted descending and non-salaried ascending by BusinessEntityID.
This example sorts employees differently based on their SalariedFlag
. Salaried employees (SalariedFlag = 1
) are sorted by BusinessEntityID
in descending order, while non-salaried employees (SalariedFlag = 0
) are sorted in ascending order.
SELECT
BusinessEntityID,
LastName,
TerritoryName,
CountryRegionName
FROM
Sales.vSalesPerson
WHERE
TerritoryName IS NOT NULL
ORDER BY
CASE CountryRegionName
WHEN 'United States' THEN TerritoryName -- Sort by TerritoryName for US
ELSE CountryRegionName -- Sort by CountryRegionName for others
END;
GO
Alt text: SQL query illustrating conditional sorting using CASE to order salespersons by TerritoryName when CountryRegionName is ‘United States’, and by CountryRegionName otherwise.
This example sorts salespersons based on CountryRegionName
. If the country is ‘United States’, it sorts by TerritoryName
; otherwise, it sorts by CountryRegionName
.
Example 4: CASE in UPDATE Statement for Conditional Updates
CASE
expressions are also valuable in UPDATE
statements to modify data conditionally.
USE AdventureWorks2022;
GO
UPDATE HumanResources.Employee
SET
VacationHours = (
CASE
WHEN ((VacationHours - 10.00) < 0) THEN VacationHours + 40.00 -- Add 40 hours if subtracting 10 results in negative hours
ELSE VacationHours + 20.00 -- Otherwise, add 20 hours
END
)
OUTPUT
DELETED.VacationHours AS BeforeVacationHours,
INSERTED.VacationHours AS AfterVacationHours;
GO
Alt text: SQL UPDATE statement demonstrating conditional updates to VacationHours using CASE, adding 40 hours if subtracting 10 results in a negative value, otherwise adding 20 hours, with output showing before and after vacation hours.
This example updates the VacationHours
for employees. It uses a CASE
statement to determine whether to add 40 or 20 hours based on whether subtracting 10 hours would result in a negative VacationHours
value.
Example 5: CASE in SET Statement within a Function
CASE
can be used within user-defined functions, particularly in SET
statements to determine variable values based on conditions.
USE AdventureWorks2022;
GO
CREATE FUNCTION dbo.GetContactInformation (@BusinessEntityID INT)
RETURNS @retContactInformation TABLE (
BusinessEntityID INT NOT NULL,
FirstName NVARCHAR(50) NULL,
LastName NVARCHAR(50) NULL,
ContactType NVARCHAR(50) NULL,
PRIMARY KEY CLUSTERED (BusinessEntityID ASC)
)
AS
-- Returns the first name, last name and contact type for the specified contact.
BEGIN
DECLARE
@FirstName NVARCHAR(50),
@LastName NVARCHAR(50),
@ContactType NVARCHAR(50);
-- Get common contact information
SELECT
@BusinessEntityID = BusinessEntityID,
@FirstName = FirstName,
@LastName = LastName
FROM
Person.Person
WHERE
BusinessEntityID = @BusinessEntityID;
SET @ContactType = CASE
-- Check for employee
WHEN EXISTS (SELECT * FROM HumanResources.Employee AS e WHERE e.BusinessEntityID = @BusinessEntityID) THEN 'Employee'
-- Check for vendor
WHEN EXISTS (SELECT * FROM Person.BusinessEntityContact AS bec WHERE bec.BusinessEntityID = @BusinessEntityID) THEN 'Vendor'
-- Check for store
WHEN EXISTS (SELECT * FROM Purchasing.Vendor AS v WHERE v.BusinessEntityID = @BusinessEntityID) THEN 'Store Contact'
-- Check for individual consumer
WHEN EXISTS (SELECT * FROM Sales.Customer AS c WHERE c.PersonID = @BusinessEntityID) THEN 'Consumer'
END;
-- Return the information to the caller
IF @BusinessEntityID IS NOT NULL
BEGIN
INSERT INTO @retContactInformation
SELECT
@BusinessEntityID,
@FirstName,
@LastName,
@ContactType;
END;
RETURN;
END;
GO
SELECT
BusinessEntityID,
FirstName,
LastName,
ContactType
FROM
dbo.GetContactInformation(2200);
GO
SELECT
BusinessEntityID,
FirstName,
LastName,
ContactType
FROM
dbo.GetContactInformation(5);
GO
Alt text: SQL code for a table-valued function GetContactInformation using CASE in a SET statement to determine ContactType based on the existence of BusinessEntityID in various tables (Employee, Vendor, Customer), followed by SELECT queries to execute the function.
In this function, the CASE
statement determines the ContactType
based on the existence of the BusinessEntityID
in different tables (Employee, Vendor, Customer). This allows the function to dynamically identify the contact type.
Example 6: CASE in HAVING Clause for Filtered Aggregates
CASE
can also be used within the HAVING
clause to filter aggregated results based on conditional logic.
USE AdventureWorks2022;
GO
SELECT
JobTitle,
MAX(ph1.Rate) AS MaximumRate
FROM
HumanResources.Employee AS e
INNER JOIN
HumanResources.EmployeePayHistory AS ph1
ON e.BusinessEntityID = ph1.BusinessEntityID
GROUP BY
JobTitle
HAVING
(
MAX(CASE WHEN SalariedFlag = 1 THEN ph1.Rate ELSE NULL END) > 40.00 -- Filter salaried employees with max rate > 40
OR
MAX(CASE WHEN SalariedFlag = 0 THEN ph1.Rate ELSE NULL END) > 15.00 -- Filter non-salaried employees with max rate > 15
)
ORDER BY
MaximumRate DESC;
GO
Alt text: SQL query using CASE in the HAVING clause to filter job titles based on maximum pay rates, differentiating between salaried and non-salaried employees, and ordering the results by MaximumRate in descending order.
This example filters job titles based on maximum pay rates, but with different thresholds for salaried and non-salaried employees. The CASE
statement within the HAVING
clause allows for this conditional filtering of aggregated data.
Conclusion: Mastering Conditional Logic with SQL Server CASE
The SQL Server CASE
statement is an indispensable tool for adding conditional logic to your SQL queries. Whether you need to categorize data, perform dynamic sorting, update records conditionally, or filter aggregated results based on complex criteria, CASE
provides the flexibility and power to achieve your goals. By understanding its syntax, formats, and nuances, you can significantly enhance your SQL querying capabilities and create more sophisticated and insightful data manipulations. Mastering CASE
is a key step in becoming proficient in SQL Server development and data analysis.