Master the two most important functions for handling NULL values in SQL.
COALESCE — Returns the first non-NULL value from a list of expressions
NULLIF — Returns NULL when two values are equal, preventing errors like division by zero
NULL is not an error — it's a state. Handle it gracefully.
What Is COALESCE?
-- Basic COALESCE: returns first non-NULL value
SELECT
first_name,
COALESCE(nickname, first_name) AS display_name
FROM employees;
-- Multiple fallbacks: department, then team, then 'Unassigned'
SELECT
employee_name,
COALESCE(department, team, 'Unassigned') AS assignment
FROM staff;
-- Replacing NULL in aggregations
SELECT
department,
COALESCE(SUM(commission), 0) AS total_commission
FROM employees
GROUP BY department;
What Is NULLIF?
-- Prevent division by zero
SELECT
order_id,
total_amount,
quantity,
total_amount / NULLIF(quantity, 0) AS unit_price
FROM order_items;
-- Convert a default value back to NULL
SELECT
employee_name,
NULLIF(bonus, 0) AS actual_bonus
FROM payroll;
-- Combined: COALESCE handles NULL, NULLIF handles zero
SELECT
employee_name,
COALESCE(NULLIF(salary, 0), 0) AS salary
FROM payroll
WHERE salary IS NOT NULL;
COALESCE vs CASE WHEN
Expression
Equivalent
COALESCE(a, b, c)
CASE WHEN a IS NOT NULL THEN a WHEN b IS NOT NULL THEN b ELSE c END
COALESCE(x, 0)
CASE WHEN x IS NOT NULL THEN x ELSE 0 END
NULLIF(a, b)
CASE WHEN a = b THEN NULL ELSE a END
Practical Examples
Employee Directory with Defaults
SELECT
employee_id,
first_name || ' ' || last_name AS full_name,
COALESCE(phone, 'No phone') AS phone,
COALESCE(email, 'No email') AS email,
COALESCE(extension, 'N/A') AS extension
FROM employees;
Salary Report with Commission Handling
SELECT
e.first_name || ' ' || e.last_name AS employee_name,
e.salary AS base_salary,
COALESCE(e.commission, 0) AS commission,
e.salary + COALESCE(e.commission, 0) AS total_compensation,
CASE
WHEN e.salary >= 100000 THEN 'Senior'
WHEN e.salary >= 60000 THEN 'Mid-Level'
ELSE 'Junior'
END AS level
FROM employees e;
Division Safety Pattern
-- Always use NULLIF when dividing to prevent runtime errors
SELECT
department_name,
total_budget,
employee_count,
total_budget / NULLIF(employee_count, 0) AS budget_per_employee
FROM department_stats;
Cleaning Import Data
-- Imported data may have 0 instead of NULL for missing values
SELECT
customer_id,
COALESCE(NULLIF(phone, ''), NULLIF(phone, '0'), 'Unknown') AS phone,
COALESCE(NULLIF(address, ''), 'No address') AS address
FROM imported_customers;
NULL Behavior Quick Reference
Expression
Result
Explanation
COALESCE(NULL, NULL, 5)
5
First non-NULL found at position 3
COALESCE(NULL, NULL, NULL)
NULL
All values are NULL
COALESCE(1, 2, 3)
1
First value is already non-NULL
NULLIF(5, 5)
NULL
Values are equal — returns NULL
NULLIF(5, 3)
5
Values differ — returns first expression
NULLIF(NULL, NULL)
NULL
Both NULL — returns NULL
COALESCE(NULLIF(5, 5), 0)
0
NULLIF returns NULL, COALESCE falls back to 0
5 / NULLIF(0, 0)
NULL
Division by zero becomes NULL propagation
Advanced Patterns
Conditional Aggregation with NULLIF
-- Calculate average salary only for departments with more than 3 employees
SELECT
department_id,
SUM(salary) / NULLIF(COUNT(*), 0) AS avg_salary
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 3;
Pivoting with COALESCE
-- Pivot monthly sales into columns with NULL replacement
SELECT
product_id,
COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 1 THEN amount END), 0) AS jan,
COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 2 THEN amount END), 0) AS feb,
COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 3 THEN amount END), 0) AS mar
FROM sales
GROUP BY product_id;
Null-Safe Comparisons
-- NULL-safe equality: COALESCE makes NULLs comparable
SELECT *
FROM employees
WHERE COALESCE(phone, '') = COALESCE(:input_phone, '');
Common Mistakes
Mistake
Why It Fails
Fix
COALESCE(NULL, NULL, NULL)
All values are NULL — returns NULL
Provide a default as the last argument
NULLIF(0, 0)
Returns NULL — may cause unexpected results downstream
Wrap with COALESCE to supply a fallback
salary / NULLIF(0, 0)
0/0 is still 0, not NULL
Ensure the divisor is checked, not the dividend
COALESCE(column, 'default') with mixed types
Type mismatch if column is INT and default is STRING
Use matching types in all positions
Using = NULL instead of IS NULL
NULL is not equal to anything — comparison always returns NULL
Use IS NULL or IS NOT NULL for NULL checks
Nested NULLIF without COALESCE
Intermediate NULL may propagate unexpectedly
Wrap with COALESCE if you need a non-NULL result
Performance Considerations
COALESCE and NULLIF are zero-cost at runtime. The database optimizer inlines them as simple comparisons. There is no performance penalty for using them liberally.
Operation
Overhead
COALESCE(a, b)
Equivalent to a single NULL check — negligible
NULLIF(a, b)
Equivalent to a = b comparison — negligible
CASE WHEN a IS NOT NULL THEN a ELSE b END
Same as COALESCE — no difference
Nested COALESCE chains (5+ values)
Still negligible — each is just a NULL check
Portability Notes
Feature
PostgreSQL
MySQL
SQL Server
Oracle
SQLite
COALESCE
✅
✅
✅
✅
✅
NULLIF
✅
✅
✅
✅
✅
ISNULL() (SQL Server)
❌
❌
✅
❌
❌
IFNULL() (MySQL)
❌
✅
❌
❌
❌
NVL() (Oracle)
❌
❌
❌
✅
❌
When to Use Each
Scenario
Use
Why
Replace NULL with a default value
COALESCE(col, default)
Clean, readable fallback
Prevent division by zero
NULLIF(divisor, 0)
Converts 0 to NULL, which propagates correctly
Convert sentinel values to NULL
NULLIF(col, sentinel)
Strips placeholder values
Multi-level fallback
COALESCE(a, b, c, d)
Chain alternatives in one expression
Conditional defaults with logic
CASE WHEN ... END
When you need complex branching beyond simple fallbacks