Case Statements

Let's review an example from the tutorial on nulls values.

SELECT
	a.award_id,
	a.award_fund,
	COALESCE(a.award_disbursed, a.award_accepted, a.award_offered) AS amount
FROM award AS a
WHERE a.award_accepted IS NULL
OR a.award_disbursed IS NULL;
award_idaward_fundamount
X00002 SEOG 20.0
X00009 SEOG 90.0
X00010 PELL 100.0
X00010 SEOG 110.0

This query selects the records with null values. It selects looks at the disbursed, accepted, and offered amounts looking for a non-null value. If it cannot find one it defaults to zero. What we really need is a column indicating the status of the award. We can do that with a CASE statement.

A CASE statement allows you to select a value based on a list of tests. The tests are the same conditional statements used in WHERE statements. A CASE statement uses the following template.

CASE
	WHEN [condition] THEN [value]
	WHEN [condition2] THEN [value]
	ELSE [default_value]
END AS column_name

We can implement this pattern like this.

SELECT
	a.award_id,
	a.award_fund,
	CASE
		WHEN a.award_disbursed IS NOT NULL THEN 'Disbursed'
		WHEN a.award_accepted IS NOT NULL THEN 'Accepted'
		WHEN a.award_offered IS NOT NULL THEN 'Offered'
		ELSE 'Cancelled'
	END AS status,
	COALESCE(a.award_disbursed, a.award_accepted, a.award_offered) AS amount
FROM award AS a
WHERE a.award_accepted IS NULL
OR a.award_disbursed IS NULL;
award_idaward_fundstatus amount
X00002 SEOG Accepted20.0
X00009 SEOG Offered 90.0
X00010 PELL Offered 100.0
X00010 SEOG Offered 110.0

The CASE statement creates the status column. It checks the disbursed amount to see if it is null. If it is not null the status value is set as the string literal 'Disbursed'. It then does the same with the accepted amount and the offered amount with the resulting values of 'Accepted' and 'Offered', respectively. If no WHEN statement resolves as logically true then the optional ELSE keyword allows a default value. If no ELSE value is provided the default is null.

WHEN statements are always resolved from the top down. Once a logically true value is found that value is used and the other tests are skipped. This is why we checked the disbursed, accepted, and offered values in that order. All of the records we selected have an offered amount. If we reverse the order of WHEN statements the status columns would all be 'Offered'.

Logical Chains of Conditionals

We can provide a WHEN statement more than one conditional if we chain the conditionals with logical operators such as AND, or OR. Suppose we want to split Pell awards into 'None', 'Small', 'Medium', and 'Large' categories.

SELECT
	a.award_id,
	a.award_fund,
	a.award_offered,
	CASE
		WHEN award_offered = 0 THEN 'None'
		WHEN award_offered > 0 AND award_offered < 100 THEN 'Small'
		WHEN award_offered >= 100 AND award_offered < 500 THEN 'Medium'
		WHEN award_offered >= 500 THEN 'Large'
	END AS size
FROM award AS a
WHERE award_fund = 'PELL'
award_idaward_fundaward_offeredsize
X00001 PELL 100.0 Medium
X00002 PELL 200.0 Medium
X00003 PELL 3000.0 Large
X00005 PELL 555.0 Large
X00008 PELL 88.0 Small
X00010 PELL 100.0 Medium

The first and last WHEN statements are similar to our previous example. The first one checks if the offered amount is zero and the last one checks if it is greater than or equal to 500.

But the second one checks if the offered amount is greater than zero AND it checks if the amount is less than 100. The third WHEN statement also has two conditionals. It checks if the value is greater than or equal to 100 AND if less than 500.

CASE vs. COALESCE()

CASE statements can also replace COALESCE() functions. COALESCE() runs through a list of values and conditionals looking for the first one that is not null. If your WHEN statements also check for nulls the behavior is identical. As an example, if you wanted to the select the offered amount with nulls replaced with zeros the following code would create two columns with identical data.

SELECT
	COALESCE(a.award_offered, 0) AS offered,
	CASE
		WHEN a.award_offered IS NOT NULL THEN a.award_offered
		ELSE 0
	END AS offered
FROM award AS a;

Different Columns

You can also use CASE statements to select from different input columns for a single output column. In this example we want the offered amounts for Pell or SEOG awards, but the accepted amounts for loans.

SELECT
	a.award_id,
	a.award_fund,
	CASE
		WHEN a.award_fund = 'PELL' THEN a.award_offered
		WHEN a.award_fund = 'SEOG' THEN a.award_offered
		WHEN a.award_fund = 'UNSUB' OR a.award_fund = 'SUB' THEN a.award_accepted
	END AS "Off_Acc"
FROM award AS a;

I decided to use a WHEN statement for Pell and another for SEOG. But for loans I used a single WHEN statement with two conditionals linked by a logical OR. Both are valid. It is just a stylistic choice if your want your CASE statement to be taller or wider.