SQL COALESCE: Replace NULL Values
COALESCE returns the first value in its list that is not NULL. Use it to show a default instead of a missing value, to fall back from one column to the next, and to stop a single NULL from turning a whole calculation into NULL.
What Is COALESCE in SQL?
Think of how you reach a customer: try their mobile first, then the home number, then the office, and if none of them is on file, note "no number". COALESCE does exactly that, from left to right, for every row. The four sample contacts below are written into the query, so you can run it and edit them:
WITH contacts(name, mobile, home, office) AS (
VALUES ('Neha', '555-0101', NULL, '555-0199'),
('Vikram', NULL, '555-0142', NULL),
('Aditi', NULL, NULL, '555-0177'),
('Rohan', NULL, NULL, NULL)
)
SELECT name, mobile, home, office,
COALESCE(mobile, home, office, 'No number on file') AS best_contact
FROM contacts;
▶ Run this query in the SQL Playground
| name | mobile | home | office | best_contact |
|---|---|---|---|---|
| Neha | 555-0101 | NULL | 555-0199 | 555-0101 |
| Vikram | NULL | 555-0142 | NULL | 555-0142 |
| Aditi | NULL | NULL | 555-0177 | 555-0177 |
| Rohan | NULL | NULL | NULL | No number on file |
Neha has a mobile number, so it is used and the other columns are never looked at. Vikram has only a home number and Aditi only an office number. Rohan has none, so he gets the final default. Without the last argument his result would be NULL.
| Business problem | COALESCE answer |
|---|---|
| Show a label instead of a blank | COALESCE(shipped_date, 'Not shipped') |
| Use the first available of several columns | COALESCE(mobile, home, office) |
| Keep a NULL from breaking a calculation | salary + COALESCE(bonus, 0) |
| Show 0 for customers with no orders after a LEFT JOIN | COALESCE(SUM(o.total), 0) |
NULL Is Not Zero and Not an Empty String
NULL means "no value recorded". Zero and an empty string are real values. The difference shows as soon as NULL meets an operator: arithmetic with NULL gives NULL, and so does a comparison, even NULL = NULL:
SELECT 5 + NULL AS plus_null,
NULL = NULL AS null_equals_null,
5 + 0 AS plus_zero,
'abc' || '' AS plus_empty_string,
COALESCE(NULL, 0) AS coalesced;
▶ Run this query in the SQL Playground
| plus_null | null_equals_null | plus_zero | plus_empty_string | coalesced |
|---|---|---|---|---|
| NULL | NULL | 5 | abc | 0 |
In the Playground, 97 orders have no shipped_date because they have not been shipped yet, and 10 employees have no manager_id. Those are the NULLs the examples below work with:
SELECT COUNT(*) AS all_orders,
COUNT(*) - COUNT(shipped_date) AS not_shipped
FROM orders;
▶ Run this query in the SQL Playground
| all_orders | not_shipped |
|---|---|
| 200 | 97 |
SELECT COUNT(*) - COUNT(manager_id) AS no_manager
FROM employees;
▶ Run this query in the SQL Playground
| no_manager |
|---|
| 10 |
To find NULLs you use IS NULL. To replace them with something else, you use COALESCE.
COALESCE Syntax and How It Works
COALESCE(value_1, value_2, ..., value_n)
| Rule | Meaning |
|---|---|
| Checked left to right | The first argument that is not NULL is returned; the rest are not evaluated |
| Any number of arguments | Two or more: columns, literals, expressions or subqueries |
| All NULL | If every argument is NULL, the result is NULL |
| Same type | All arguments should have compatible types, such as all text or all numbers |
| Read-only | COALESCE changes the query result, not the data stored in the table |
SELECT COALESCE(NULL, NULL, 'SQLab Hub', 'never reached') AS first_value,
COALESCE(NULL, NULL) AS all_null;
▶ Run this query in the SQL Playground
| first_value | all_null |
|---|---|
| SQLab Hub | NULL |
The first column skips two NULLs and returns SQLab Hub; the last argument is never reached. The second column has nothing but NULLs, so the result is NULL. COALESCE is part of standard SQL and works the same in MySQL, PostgreSQL, SQL Server, Oracle and SQLite.
Show a Default Instead of NULL
The most common use is a readable label for a missing value. Orders that have not shipped get "Not shipped yet" instead of a blank cell:
SELECT id, status,
COALESCE(shipped_date, 'Not shipped yet') AS shipped
FROM orders
ORDER BY id
LIMIT 6;
▶ Run this query in the SQL Playground
| id | status | shipped |
|---|---|---|
| 1 | cancelled | Not shipped yet |
| 2 | delivered | 2023-04-09 |
| 3 | delivered | 2023-07-10 |
| 4 | processing | Not shipped yet |
| 5 | delivered | 2023-12-21 |
| 6 | shipped | 2024-12-13 |
Here the fallback is text while shipped_date is a date. SQLite stores dates as text, so this works. PostgreSQL and SQL Server require one type for all arguments, so there you convert the date first: COALESCE(CAST(shipped_date AS VARCHAR(10)), 'Not shipped yet').
A fallback from another table
With a self join, every employee can be shown next to their manager. Employees without one get a label instead of NULL:
SELECT e.first_name || ' ' || e.last_name AS employee,
e.title,
COALESCE(m.first_name || ' ' || m.last_name, 'No manager') AS manager
FROM employees e
LEFT JOIN employees m ON m.id = e.manager_id
ORDER BY e.id
LIMIT 6;
▶ Run this query in the SQL Playground
| employee | title | manager |
|---|---|---|
| William Lewis | Senior CS Manager | No manager |
| John Miller | Paralegal | No manager |
| Daniel Rivera | VP Engineering | No manager |
| Melissa Smith | VP Engineering | No manager |
| Jason Mitchell | Product Designer | No manager |
| Amy Nguyen | Product Designer | No manager |
For the top-level employees, m.first_name || ' ' || m.last_name is NULL because the joined manager row does not exist, so COALESCE moves on to the label.
COALESCE in Calculations
Any arithmetic with NULL returns NULL. If a bonus was never recorded, salary + bonus wipes out the whole total. COALESCE replaces the missing bonus with 0 before the addition:
WITH pay(name, salary, bonus) AS (
VALUES ('Aditi', 112000, 15000),
('Rohan', 58000, NULL),
('Priya', 97000, 8000),
('Karan', 46000, NULL),
('Sneha', 63000, 0)
)
SELECT name, salary, bonus,
salary + bonus AS total_without_coalesce,
salary + COALESCE(bonus, 0) AS total_pay
FROM pay;
▶ Run this query in the SQL Playground
| name | salary | bonus | total_without_coalesce | total_pay |
|---|---|---|---|---|
| Aditi | 112000 | 15000 | 127000 | 127000 |
| Rohan | 58000 | NULL | NULL | 58000 |
| Priya | 97000 | 8000 | 105000 | 105000 |
| Karan | 46000 | NULL | NULL | 46000 |
| Sneha | 63000 | 0 | 63000 | 63000 |
Rohan and Karan have no bonus recorded, so their first total is NULL. Sneha's bonus is a real 0, and her total works either way. COALESCE makes the missing bonus behave like hers.
COALESCE With SUM, AVG and COUNT
Aggregate functions already skip NULLs, so COALESCE inside them changes the answer, not only the display. Compare the bonus figures for the same five people:
WITH pay(name, salary, bonus) AS (
VALUES ('Aditi', 112000, 15000),
('Rohan', 58000, NULL),
('Priya', 97000, 8000),
('Karan', 46000, NULL),
('Sneha', 63000, 0)
)
SELECT SUM(bonus) AS sum_bonus,
SUM(COALESCE(bonus, 0)) AS sum_coalesced,
ROUND(AVG(bonus), 2) AS avg_bonus,
AVG(COALESCE(bonus, 0)) AS avg_coalesced,
COUNT(bonus) AS count_bonus,
COUNT(COALESCE(bonus, 0)) AS count_coalesced
FROM pay;
▶ Run this query in the SQL Playground
| sum_bonus | sum_coalesced | avg_bonus | avg_coalesced | count_bonus | count_coalesced |
|---|---|---|---|---|---|
| 23000 | 23000 | 7666.67 | 4600 | 3 | 5 |
| Aggregate | Without COALESCE | With COALESCE(bonus, 0) |
|---|---|---|
| SUM | 23000: NULLs are skipped | 23000: the same, adding zeros changes nothing |
| AVG | 7666.67: average of the 3 recorded bonuses | 4600: average over all 5 people |
| COUNT | 3: rows that have a bonus | 5: every row |
Neither average is "correct" on its own. AVG(bonus) answers "what is the typical bonus among people who got one?"; AVG(COALESCE(bonus, 0)) answers "what is the average bonus per employee?". Decide which question you are asking before you add COALESCE.
SUM over no rows
SUM of zero rows is NULL, not 0. That matters for dashboard tiles. There are no orders in 2025 in the Playground, so the plain SUM is NULL and the wrapped one shows 0:
SELECT SUM(total) AS plain_sum,
COALESCE(SUM(total), 0) AS revenue_2025
FROM orders
WHERE order_date >= '2025-01-01';
▶ Run this query in the SQL Playground
| plain_sum | revenue_2025 |
|---|---|
| NULL | 0 |
COALESCE After a LEFT JOIN
A LEFT JOIN keeps every row from the left table and fills the columns of the right table with NULL when there is no match. This lists customers with their order count and total spend, including customers who never ordered:
SELECT u.id, u.first_name, u.last_name,
COUNT(o.id) AS orders,
SUM(o.total) AS raw_spend,
COALESCE(ROUND(SUM(o.total), 2), 0) AS total_spend
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id, u.first_name, u.last_name
ORDER BY u.id
LIMIT 6;
▶ Run this query in the SQL Playground
| id | first_name | last_name | orders | raw_spend | total_spend |
|---|---|---|---|---|---|
| 1 | Jennifer | Torres | 1 | 4064.75 | 4064.75 |
| 2 | Donna | Adams | 1 | 1618.29 | 1618.29 |
| 3 | Kenneth | Young | 0 | NULL | 0 |
| 4 | Michael | Carter | 3 | 4863.29 | 4863.29 |
| 5 | Robert | Campbell | 1 | 4924.23 | 4924.23 |
| 6 | Deborah | Jackson | 2 | 7403.780000000001 | 7403.78 |
For a customer with no orders, SUM(o.total) is NULL and COALESCE turns it into 0. COUNT(o.id) is already 0, because COUNT never returns NULL. 36 of the 150 customers have never ordered:
SELECT COUNT(*) AS customers_without_orders
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.id IS NULL;
▶ Run this query in the SQL Playground
| customers_without_orders |
|---|
| 36 |
When 0 would be misleading
Products without reviews have no average rating. Showing 0 would look like a terrible rating, so a label is the honest default:
SELECT p.name,
COUNT(r.id) AS reviews,
COALESCE(CAST(ROUND(AVG(r.rating), 1) AS TEXT), 'No reviews yet') AS avg_rating
FROM products p
LEFT JOIN reviews r ON r.product_id = p.id
GROUP BY p.id, p.name
ORDER BY reviews, p.id
LIMIT 6;
▶ Run this query in the SQL Playground
| name | reviews | avg_rating |
|---|---|---|
| Giant Stuffed Teddy Bear | 0 | No reviews yet |
| Merino Wool Sweater | 0 | No reviews yet |
| Retinol Eye Cream 30ml | 0 | No reviews yet |
| Play Kitchen Deluxe | 0 | No reviews yet |
| Resistance Bands Set | 0 | No reviews yet |
| Back Seat Organizer | 0 | No reviews yet |
COALESCE With CASE WHEN and Subqueries
A NULL inside a CASE condition makes that condition unknown, so the row falls through to ELSE without anyone noticing. Wrapping the column in COALESCE makes the intent explicit:
WITH pay(name, salary, bonus) AS (
VALUES ('Aditi', 112000, 15000),
('Rohan', 58000, NULL),
('Priya', 97000, 8000),
('Karan', 46000, NULL),
('Sneha', 63000, 0)
)
SELECT name, bonus,
CASE WHEN COALESCE(bonus, 0) >= 10000 THEN 'High bonus'
WHEN COALESCE(bonus, 0) > 0 THEN 'Bonus'
ELSE 'No bonus'
END AS bonus_group
FROM pay;
▶ Run this query in the SQL Playground
| name | bonus | bonus_group |
|---|---|---|
| Aditi | 15000 | High bonus |
| Rohan | NULL | No bonus |
| Priya | 8000 | Bonus |
| Karan | NULL | No bonus |
| Sneha | 0 | No bonus |
COALESCE is shorthand for one particular CASE: COALESCE(a, b) means CASE WHEN a IS NOT NULL THEN a ELSE b END.
A subquery as an argument
Any argument can be a scalar subquery. This shows each customer's latest order date, or a label if there is none:
SELECT u.first_name, u.last_name,
COALESCE((SELECT MAX(o.order_date)
FROM orders o
WHERE o.user_id = u.id), 'Never ordered') AS last_order
FROM users u
ORDER BY u.id
LIMIT 6;
▶ Run this query in the SQL Playground
| first_name | last_name | last_order |
|---|---|---|
| Jennifer | Torres | 2022-09-26 |
| Donna | Adams | 2023-04-02 |
| Kenneth | Young | Never ordered |
| Michael | Carter | 2024-11-15 |
| Robert | Campbell | 2024-05-18 |
| Deborah | Jackson | 2023-01-19 |
COALESCE With NULLIF: Safe Division
NULLIF is close to the opposite of COALESCE: NULLIF(a, b) returns NULL when a equals b. Together they make a safe division. NULLIF turns a zero denominator into NULL, so the division gives NULL instead of an error, and COALESCE turns that NULL into a default:
SELECT u.id, u.first_name,
COUNT(o.id) AS orders,
COALESCE(ROUND(SUM(o.total) / NULLIF(COUNT(o.id), 0), 2), 0) AS avg_order_value
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id, u.first_name
ORDER BY u.id
LIMIT 6;
▶ Run this query in the SQL Playground
| id | first_name | orders | avg_order_value |
|---|---|---|---|
| 1 | Jennifer | 1 | 4064.75 |
| 2 | Donna | 1 | 1618.29 |
| 3 | Kenneth | 0 | 0 |
| 4 | Michael | 3 | 1621.1 |
| 5 | Robert | 1 | 4924.23 |
| 6 | Deborah | 2 | 3701.89 |
SQLite returns NULL for a division by zero anyway, but PostgreSQL and SQL Server raise an error, so the NULLIF is what makes this query portable.
COALESCE vs ISNULL, IFNULL, NVL and CASE
Most databases have a two-argument shortcut for the same job. They behave like COALESCE(a, b), with small differences:
| Function | Databases | Arguments | Notes |
|---|---|---|---|
| COALESCE(a, b, ...) | All of them (standard SQL) | Any number | The portable choice |
| ISNULL(a, b) | SQL Server | Exactly 2 | The result takes the type of a, so a longer text in b can be cut off. In MySQL, ISNULL(a) is a different function that returns 1 or 0 |
| IFNULL(a, b) | MySQL, SQLite | Exactly 2 | Same result as COALESCE(a, b) |
| NVL(a, b) | Oracle | Exactly 2 | Oracle also has NVL2(a, if_not_null, if_null) |
| CASE WHEN a IS NULL THEN b ELSE a END | All of them | Any logic | Longer; use it when the rule is more than "replace NULL" |
In the Playground, IFNULL and COALESCE give the same answer:
SELECT id,
IFNULL(shipped_date, 'Not shipped') AS with_ifnull,
COALESCE(shipped_date, 'Not shipped') AS with_coalesce
FROM orders
ORDER BY id
LIMIT 4;
▶ Run this query in the SQL Playground
| id | with_ifnull | with_coalesce |
|---|---|---|
| 1 | Not shipped | Not shipped |
| 2 | 2023-04-09 | 2023-04-09 |
| 3 | 2023-07-10 | 2023-07-10 |
| 4 | Not shipped | Not shipped |
Prefer COALESCE unless a team standard says otherwise: it takes any number of arguments and runs unchanged if the query moves to another database.
COALESCE and Performance
COALESCE in SELECT costs almost nothing. In WHERE it can hurt: like any function wrapped around a column, it usually stops the database from using an index on that column.
| Filter | Index-friendly? | Better form |
|---|---|---|
WHERE COALESCE(mobile, home) = '555-0142' | Usually not | WHERE mobile = '555-0142' OR (mobile IS NULL AND home = '555-0142') |
WHERE COALESCE(discount, 0) = 0 | Usually not | WHERE discount = 0 OR discount IS NULL |
Check with EXPLAIN before rewriting, and only bother on large tables.
Common COALESCE Mistakes
Six errors that give wrong results, most of them without an error message.
1. Putting the default first
SELECT COALESCE('Not shipped', shipped_date)
FROM orders;
SELECT COALESCE(shipped_date, 'Not shipped')
FROM orders;
COALESCE returns the first non-NULL argument. A literal is never NULL, so if it comes first, the column is never used.
2. Confusing NULL with 0 in a filter
SELECT *
FROM pay
WHERE bonus = 0;
SELECT *
FROM pay
WHERE bonus = 0 OR bonus IS NULL;
bonus = 0 is unknown for NULL rows, so they are left out. Say explicitly whether "no bonus recorded" should count as zero.
3. Adding a nullable column without COALESCE
SELECT name, salary + bonus AS total_pay
FROM pay;
SELECT name, salary + COALESCE(bonus, 0) AS total_pay
FROM pay;
Arithmetic with NULL gives NULL, as the calculations example above shows for Rohan and Karan.
4. Forgetting COALESCE after a LEFT JOIN
SELECT u.id, SUM(o.total) AS spend
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id;
SELECT u.id, COALESCE(SUM(o.total), 0) AS spend
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id;
The LEFT JOIN keeps the customer, but SUM over no matching rows is NULL.
5. Choosing a default that invents data
SELECT id, COALESCE(shipped_date, CURRENT_DATE) AS shipped
FROM orders;
SELECT id, COALESCE(shipped_date, 'Not shipped') AS shipped
FROM orders;
A fallback that looks like real data is worse than a blank. Use a label, or leave the NULL, when no honest default exists.
6. Mixing data types
SELECT COALESCE(shipped_date, 'Not shipped')
FROM orders; -- shipped_date is a DATE column
SELECT COALESCE(CAST(shipped_date AS VARCHAR(10)), 'Not shipped')
FROM orders;
Databases with real date types cannot put a date and free text in one column. SQLite stores dates as text, which is why the shorter form runs in the Playground.
COALESCE Cheat Sheet
| Task | Pattern |
|---|---|
| Default text | COALESCE(shipped_date, 'Not shipped') |
| Fallback chain | COALESCE(mobile, home, office, 'No number') |
| Safe arithmetic | salary + COALESCE(bonus, 0) |
| 0 instead of NULL after LEFT JOIN | COALESCE(SUM(o.total), 0) |
| Average over everyone, not only recorded values | AVG(COALESCE(bonus, 0)) |
| Safe division | COALESCE(a / NULLIF(b, 0), 0) |
| Label for a missing join | COALESCE(m.first_name, 'No manager') |
Practice COALESCE Queries
6 exercises on the real Playground tables.
Write each query yourself first. Every solution has been run against the Playground database.
-
1Easy List the 8 most recent orders with their shipped date, showing "Awaiting shipment" when there is none. Table:
ordersShow solution
SELECT id, status, COALESCE(shipped_date, 'Awaiting shipment') AS shipped FROM orders ORDER BY order_date DESC, id DESC LIMIT 8;▶ Run this query in the SQL Playground
COALESCE returns shipped_date when it exists and the label otherwise.
-
2Easy Show every employee's manager_id, with 0 for employees who have no manager. List the first 10 by id. Table:
employeesShow solution
SELECT id, first_name, COALESCE(manager_id, 0) AS manager_id FROM employees ORDER BY id LIMIT 10;▶ Run this query in the SQL Playground
A numeric default keeps the column numeric. 0 is safe here because no employee has id 0.
-
3Medium For each product category, show the units sold, with 0 for any product that was never ordered counted in. Sort by units sold. Table:
products, order_itemsShow solution
SELECT p.category, COALESCE(SUM(oi.quantity), 0) AS units_sold FROM products p LEFT JOIN order_items oi ON oi.product_id = p.id GROUP BY p.category ORDER BY units_sold DESC;▶ Run this query in the SQL Playground
The LEFT JOIN keeps unsold products; COALESCE matters for any group whose SUM has nothing to add.
-
4Medium Show the 8 customers with the lowest total successful payments, including customers with no successful payment (shown as 0). Table:
users, paymentsShow solution
SELECT u.id, u.first_name, u.last_name, COALESCE(ROUND(SUM(p.amount), 2), 0) AS paid FROM users u LEFT JOIN payments p ON p.user_id = u.id AND p.status = 'success' GROUP BY u.id, u.first_name, u.last_name ORDER BY paid, u.id LIMIT 8;▶ Run this query in the SQL Playground
The status filter sits in ON, not WHERE, so customers without a successful payment stay in the result.
-
5Medium Show each employee with their manager's first name, or "Top level" if they have none. First 8 by id. Table:
employeesShow solution
SELECT e.first_name AS employee, COALESCE(m.first_name, 'Top level') AS manager FROM employees e LEFT JOIN employees m ON m.id = e.manager_id ORDER BY e.id LIMIT 8;▶ Run this query in the SQL Playground
For employees without a manager the joined row is missing, so m.first_name is NULL.
-
6Hard For each category, show the number of products and the share (in percent) of products that have at least one review. Table:
products, reviewsShow solution
SELECT p.category, COUNT(*) AS products, ROUND(100.0 * SUM(CASE WHEN COALESCE(r.reviews, 0) > 0 THEN 1 ELSE 0 END) / COUNT(*), 1) AS pct_reviewed FROM products p LEFT JOIN (SELECT product_id, COUNT(*) AS reviews FROM reviews GROUP BY product_id) r ON r.product_id = p.id GROUP BY p.category ORDER BY pct_reviewed DESC, p.category;▶ Run this query in the SQL Playground
The subquery counts reviews per product; for products without reviews the LEFT JOIN gives NULL, which COALESCE turns into 0 before the comparison.
📝 Quiz — COALESCE
Test your understanding · 0/30 answered
What does COALESCE return?
COALESCE checks its arguments from left to right and returns the first one that is not NULL.
What does COALESCE(NULL, NULL, 'SQLab Hub', 'Other') return?
It skips the two NULLs and returns 'SQLab Hub'. The last argument is never reached.
What does COALESCE return if every argument is NULL?
With no non-NULL value to return, the result is NULL.
Is NULL the same as 0?
A NULL bonus means no bonus was recorded; a bonus of 0 is a recorded value of zero.
Is NULL the same as an empty string ''?
Most databases treat them differently. Oracle is the exception: it stores '' as NULL.
How do you show 'Not Available' instead of a NULL phone?
COALESCE(column, default) returns the column when it has a value and the default otherwise.
Can COALESCE take more than two arguments?
That is its main advantage over ISNULL, IFNULL and NVL, which take exactly two.
What does COALESCE(mobile, home, office) implement?
It returns the first of the three that is not NULL.
How does SUM(COALESCE(amount, 0)) differ from SUM(amount)?
SUM already skips NULLs, so adding zeros does not change a total. It only matters when every value is NULL or there are no rows.
What does salary + COALESCE(bonus, 0) return when bonus is NULL?
COALESCE turns the missing bonus into 0 before the addition.
What does salary + bonus return when bonus is NULL?
Any arithmetic with NULL gives NULL.
Is COALESCE standard SQL?
ISNULL (SQL Server), IFNULL (MySQL, SQLite) and NVL (Oracle) are vendor-specific; COALESCE is standard.
What is the main difference between COALESCE and ISNULL?
SQL Server's ISNULL also takes the data type of its first argument, which can cut off a longer replacement text.
What is the difference between COALESCE and IFNULL?
IFNULL(a, b) gives the same result as COALESCE(a, b).
What is NVL?
NVL is Oracle-specific and takes exactly two arguments.
Can COALESCE be used inside a CASE WHEN condition?
It makes the treatment of NULL explicit instead of letting the row fall through to ELSE.
What does COALESCE(delivery_date, expected_delivery_date) return when delivery_date is NULL?
It falls back to the second column.
How is COALESCE used with a LEFT JOIN?
COALESCE(SUM(o.total), 0) shows 0 for customers without orders.
What is a common mistake with argument order?
COALESCE('N/A', phone) always returns 'N/A', because a literal is never NULL.
Why should all COALESCE arguments have compatible types?
PostgreSQL and SQL Server reject a date and free text in one COALESCE without a CAST.
What does COUNT(COALESCE(email, '')) count, compared with COUNT(email)?
COUNT skips NULLs, so wrapping the column changes what is counted.
What is a good use of COALESCE(primary_email, secondary_email, backup_email)?
It is the same fallback pattern as with phone numbers.
How do you make WHERE COALESCE(mobile, home) = :x more index-friendly?
A function around a filtered column usually blocks a plain index; direct conditions do not.
What can EXPLAIN tell you about a query with COALESCE in WHERE?
EXPLAIN shows the query plan, including index use.
Which databases support COALESCE?
It is standard SQL and behaves the same in all of them.
How do analysts use COALESCE in reports?
It is one of the most common tools for blank-free report columns.
Is COALESCE a common interview topic?
NULL handling is tested often, and COALESCE is the standard answer.
How does COALESCE(col, x) compare with CASE WHEN col IS NULL THEN x ELSE col END?
For plain NULL replacement they are equivalent.
Why does choosing COALESCE over ISNULL or NVL matter?
Portability is the main reason to prefer the standard function.
How are COALESCE and NULLIF related?
Combined as COALESCE(a / NULLIF(b, 0), 0), they give a safe division.
COALESCE Interview Questions
Q1. What does COALESCE do, and what does it return when every argument is NULL?
It returns the first argument that is not NULL, checking from left to right. If all arguments are NULL, it returns NULL.
Q2. How would you show 0 instead of NULL for customers without orders?
LEFT JOIN orders to users, group by the user, and wrap the aggregate: COALESCE(SUM(o.total), 0). COUNT(o.id) already returns 0, so it needs no COALESCE.
Q3. Does COALESCE inside SUM change the total?
No. SUM skips NULLs, so adding zeros changes nothing, except that the result is 0 instead of NULL when there are no values at all. Inside AVG and COUNT it does change the answer, because the NULL rows are then counted.
Q4. What is the difference between COALESCE, ISNULL, IFNULL and NVL?
COALESCE is standard SQL and takes any number of arguments. ISNULL (SQL Server), IFNULL (MySQL, SQLite) and NVL (Oracle) take exactly two. SQL Server's ISNULL also returns the type of its first argument, which can truncate the replacement.
Q5. How do you avoid a division-by-zero error and a NULL result at the same time?
COALESCE(a / NULLIF(b, 0), 0). NULLIF turns a zero denominator into NULL, so the division returns NULL instead of failing, and COALESCE replaces that NULL with 0.
Q6. Why can COALESCE in WHERE be slow?
A function around a column usually stops the database from using an index on it. Rewrite WHERE COALESCE(a, b) = x as direct conditions on a and b when the table is large.
Q7. How would you build a cascading default, such as a customer price, then a region price, then a list price?
COALESCE(customer_price, region_price, list_price). The most specific value goes first and the most general last.
Q8. When is 0 the wrong default for a NULL?
Whenever 0 is a meaningful value that would be mistaken for real data, such as a rating, a price or a test score. Then a label such as "not recorded", or leaving the NULL, is more honest.
Frequently Asked Questions
What is COALESCE in SQL?
COALESCE is a function that returns the first non-NULL value from a list of expressions. If every expression is NULL, it returns NULL. It is the standard way to supply a fallback for missing data.
Why do we use COALESCE?
To replace missing values with a sensible default, so that reports show a label instead of a blank and calculations do not turn into NULL because one input is missing.
How does COALESCE work?
COALESCE checks its arguments from left to right and returns the first one that is not NULL. For example COALESCE(NULL, NULL, 'SQLab Hub', 'Other') returns 'SQLab Hub'.
What is the difference between COALESCE and ISNULL?
COALESCE is standard SQL and accepts any number of arguments. ISNULL is specific to SQL Server and takes exactly two; MySQL has a different ISNULL that only tests whether a value is NULL.
What is the difference between COALESCE and IFNULL?
IFNULL, available in MySQL and SQLite, works like COALESCE with exactly two arguments. COALESCE can chain any number of fallback values.
Can COALESCE handle multiple columns?
Yes. COALESCE(col1, col2, col3) returns the first of the columns that has a value, which is how a mobile, home, office phone fallback is built.
How do data analysts use COALESCE?
To label missing data before reporting, to show 0 instead of NULL after a LEFT JOIN, to keep calculations working when one input is missing, and to build clean columns for dashboards.
Is COALESCE important in SQL interviews?
Yes. Typical questions cover COALESCE versus ISNULL, IFNULL and NVL, COALESCE after a LEFT JOIN, and how COALESCE changes SUM, AVG and COUNT.