CASE, COALESCE and NULLIF
Part of the free LogicWiz SQL & Databases course, Module 3: Functions and expressions. The queries and practice run in the app on real Postgres.
Meher wants the menu to say Budget, Regular or Premium next to each dish, and New where a restaurant has no rating yet. None of those words are stored anywhere. SQL can make them up as it reads each row.
You'll learn to
- Turn values into labels with CASE WHEN … THEN … ELSE … END
- Order the WHEN branches so the right one wins
- Replace NULL with COALESCE and avoid divide-by-zero with NULLIF
Learn
CASE: if-else inside a query
CASE checks conditions for each row and gives back one value:
CASE
WHEN condition1 THEN value1
WHEN condition2 THEN value2
ELSE value3
END
It is an expression, like price * 2, so it can go in SELECT, WHERE, ORDER BY, and later inside COUNT and SUM. Always close it with END, and give the new column a name with AS.
Price bands for the menu
SELECT name,
price,
CASE
WHEN price >= 300 THEN 'Premium'
WHEN price >= 100 THEN 'Regular'
ELSE 'Budget'
END AS band
FROM menu_items
ORDER BY price DESC;
Butter Chicken (₹450) passes the first WHEN and gets Premium. Masala Dosa (₹120) fails it, passes the second, and gets Regular.
The first true WHEN wins
Postgres tries the WHEN branches from the top and stops at the first one that is true. The rest are never checked.
So put the strictest condition first. If price >= 100 came first, a ₹450 dish would stop there and be called Regular.
If no WHEN is true and there is no ELSE, the result is NULL. Add an ELSE unless a NULL is what you want.
The short form: CASE column WHEN value
SELECT order_id,
status,
CASE status
WHEN 'delivered' THEN 'Done'
WHEN 'cancelled' THEN 'Refund'
ELSE 'Track it'
END AS next_step
FROM orders
WHERE order_id >= 1018;
When every branch checks one column for equality, write the column once after CASE. It only does =, so it cannot test ranges, and it cannot match NULL.
COALESCE: the first value that is not NULL
SELECT name,
rating,
COALESCE(rating::text, 'New') AS shown_rating
FROM restaurants;
COALESCE(a, b, c) gives back the first argument that is not NULL. Pizza Corner has no rating, so it shows New.
Every argument must be the same type
SELECT name, COALESCE(rating, 'New') AS shown_rating
FROM restaurants;
rating is a number, so Postgres tries to read 'New' as a number and fails. Cast the number to text first: COALESCE(rating::text, 'New').
NULLIF: turn a value into NULL
SELECT order_id,
delivery_fee,
NULLIF(delivery_fee, 0) AS fee_or_null,
500 / NULLIF(delivery_fee, 0) AS orders_per_500
FROM orders
WHERE order_id <= 1005;
NULLIF(a, b) gives NULL when a = b, otherwise a. Dividing by zero is an error that stops the whole query, but dividing by NULL just gives NULL. So x / NULLIF(y, 0) is the safe way to divide.
Interviews ask: bucket salaries or marks into grades with CASE, swap two values in one UPDATE (SET gender = CASE gender WHEN 'M' THEN 'F' ELSE 'M' END), and avoid divide-by-zero with NULLIF. University exams like asking what CASE gives when nothing matches: NULL.
In one line
CASE WHEN … THEN … ELSE … END labels rows (first true WHEN wins); COALESCE fills NULLs; x / NULLIF(y, 0) divides safely.