<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. SQL
  3. Functions and expressions
All lessons in this course

Lesson 16 of 54 13 minBeginner

Decisions inside a query: CASE and IIF

Turn values into labels, bands and custom sort orders with CASE. Learn the two forms, why the order of WHENs matters, what a missing ELSE does, and the shortcuts IIF and CHOOSE.

CASE is SQL’s if-then-else. It is an expression: it produces one value per row, so it can go anywhere a value can: in SELECT, ORDER BY, WHERE, even inside other functions.

Searched CASE

The searched form tests a list of conditions. SQL Server tries them from the top and returns the result of the first one that is true:

Query.sql
SELECT Name, Stock,       CASE           WHEN Stock = 0   THEN N'Out of stock'           WHEN Stock < 20  THEN N'Low'           WHEN Stock < 100 THEN N'OK'           ELSE N'Plenty'       END AS StockLevelFROM dbo.ProductsWHERE ProductId <= 6 OR Stock = 0;
Output
Name              Stock  StockLevel
----------------  -----  ------------
Claw hammer          40  OK
Cordless drill       12  Low
Tape measure 8 m     65  OK
Cement 25 kg        300  Plenty
Sand 25 kg          220  Plenty
Timber 2x4 2.4 m    500  Plenty
Wall plugs (100)      0  Out of stock
Safety goggles        0  Out of stock

Reorder the WHENs and switch the ELSE off to see why the order matters:

Interactive

CASE: the first true WHEN wins

CASE
  WHEN Stock = 0 THEN N'Out of stock'2 rows
  WHEN Stock < 20 THEN N'Low'1 row
  WHEN Stock < 100 THEN N'OK'5 rows
  ELSE N'Plenty'4 rows
END AS StockLevel
NameStockStockLevelMatched by
1Claw hammer40OKWHEN Stock < 100
2Cordless drill12LowWHEN Stock < 20
3Tape measure 8 m65OKWHEN Stock < 100
4Cement 25 kg300PlentyELSE
5Sand 25 kg220PlentyELSE
6Timber 2x4 2.4 m500PlentyELSE
7Plywood sheet 18 mm80OKWHEN Stock < 100
8Wood screws (200)150PlentyELSE
9Wall plugs (100)0Out of stockWHEN Stock = 0
10Masonry paint 10 L25OKWHEN Stock < 100
11Paint roller set30OKWHEN Stock < 100
12Safety goggles0Out of stockWHEN Stock = 0
SQL Server tries each WHEN from the top and stops at the first one that is true, so “Stock < 100” only sees rows that were not already caught. Move the WHENs to see the order matter.

Simple CASE

When every test compares the same expression with =, the simple form is shorter. It cannot test ranges or NULL (because = NULL is never true):

Query.sql
SELECT OrderId, Status,       CASE Status           WHEN 'Shipped'   THEN N'On its way or delivered'           WHEN 'Pending'   THEN N'Waiting to be packed'           WHEN 'Cancelled' THEN N'Cancelled by customer'           ELSE N'Check status'       END AS ForCustomerFROM dbo.OrdersWHERE OrderId IN (5, 6, 8);
Output
OrderId  Status     ForCustomer
-------  ---------  -----------------------
      5  Shipped    On its way or delivered
      6  Cancelled  Cancelled by customer
      8  Pending    Waiting to be packed

CASE in ORDER BY: a custom sort

The warehouse wants pending orders first, then shipped, then cancelled. Alphabetical order would not give that, but a CASE that turns each status into a number does:

Query.sql
SELECT OrderId, OrderDate, StatusFROM dbo.OrdersORDER BY CASE Status WHEN 'Pending' THEN 1 WHEN 'Shipped' THEN 2 ELSE 3 END,         OrderDate DESC;
Output
OrderId  OrderDate   Status
-------  ----------  ---------
      9  2024-06-01  Pending
      8  2024-05-18  Pending
     10  2024-06-12  Shipped
      7  2024-05-05  Shipped
      5  2024-04-02  Shipped
      4  2024-03-15  Shipped
      3  2024-03-01  Shipped
      2  2024-02-10  Shipped
      1  2024-02-03  Shipped
      6  2024-04-20  Cancelled

One result type

All the results of a CASE must share one type. If they differ, SQL Server converts them all to the type with the highest precedence, and int beats text. Mixing a number and a word therefore fails as soon as the word comes up:

Query.sql
SELECT Name,       CASE WHEN Stock > 0 THEN Stock ELSE 'none' END AS StockShownFROM dbo.Products;
Output
Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value 'none' to data type int.

Make every branch text instead: CASE WHEN Stock > 0 THEN CAST(Stock AS varchar(10)) ELSE 'none' END. Type precedence is the subject of the next lesson.

IIF and CHOOSE

IIF(test, ifTrue, ifFalse) is shorthand for a CASE with one WHEN and an ELSE. CHOOSE(n, a, b, c, …) picks the nth item of a list (counting from 1). Both are rewritten into CASE behind the scenes.

Query.sql
SELECT Name, Price,       IIF(Price >= 40, N'Big ticket', N'Everyday') AS Kind,       CHOOSE(CategoryId, N'Tools', N'Building', N'Timber', N'Fixings', N'Paint', N'Safety') AS CategoryFROM dbo.ProductsWHERE ProductId IN (2, 5, 7, 11);
Output
Name                 Price  Kind        Category
-------------------  -----  ----------  --------
Cordless drill       89.00  Big ticket  Tools
Sand 25 kg            4.10  Everyday    Building
Plywood sheet 18 mm  42.00  Big ticket  Timber
Paint roller set     12.50  Everyday    Paint
Quick check

A product has Stock = 0. What does CASE WHEN Stock < 100 THEN 'OK' WHEN Stock = 0 THEN 'Out' END return?

Quick check

What does a CASE return for a row that matches no WHEN and has no ELSE?

Practice

Put every product into a price band: under 5 is Budget, under 20 is Standard, under 60 is Premium, anything else is Trade only. Show Name, Price and Band, sorted by price.

Show a hint

A searched CASE with the WHENs in rising order, so each catches only what the previous ones did not.

Show the solution
Solution.sql
SELECT Name, Price,       CASE           WHEN Price < 5  THEN N'Budget'           WHEN Price < 20 THEN N'Standard'           WHEN Price < 60 THEN N'Premium'           ELSE N'Trade only'       END AS BandFROM dbo.ProductsORDER BY Price;
Output
Name                 Price  Band
-------------------  -----  ----------
Sand 25 kg            4.10  Budget
Wall plugs (100)      4.50  Budget
Timber 2x4 2.4 m      6.80  Standard
Safety goggles        6.80  Standard
Cement 25 kg          7.20  Standard
Tape measure 8 m      9.75  Standard
Wood screws (200)    11.90  Standard
Paint roller set     12.50  Standard
Claw hammer          18.50  Standard
Plywood sheet 18 mm  42.00  Premium
Masonry paint 10 L   54.00  Premium
Cordless drill       89.00  Trade only

Remember

  • CASE is an expression: one value per row, usable in SELECT, ORDER BY, WHERE and inside functions.
  • Searched CASE (CASE WHEN cond THEN …) tests conditions; simple CASE (CASE x WHEN value THEN …) compares one expression for equality.
  • WHENs are tried top to bottom and the first true one wins: put the narrowest first.
  • No ELSE means NULL for unmatched rows; all branches must share one type.
  • IIF and CHOOSE are shortcuts that become CASE.
0 of 54 lessons completed