SQL IS NULL and IS NOT NULL: Find Missing Values
In SQL, NULL means a value is missing or unknown, and you test for it with IS NULL or IS NOT NULL. A comparison such as = NULL never matches anything, because NULL is not equal to any value, not even to another NULL.
What Is NULL in SQL?
NULL is a marker for "no value here". It is different from zero and from an empty string: a salary of 0 is a known number, while a NULL salary has not been recorded. In the Playground, an employee with no manager has NULL in manager_id. Find those employees with IS NULL:
SELECT first_name, last_name, title
FROM employees
WHERE manager_id IS NULL
ORDER BY last_name, first_name;
▶ Run this query in the SQL Playground
| first_name | last_name | title |
|---|---|---|
| Steven | Baker | Paralegal |
| Michael | Campbell | Senior Counsel |
| William | Lewis | Senior CS Manager |
| John | Miller | Paralegal |
| Michelle | Miller | COO |
| Jason | Mitchell | Product Designer |
10 of the 120 employees have no manager recorded.
WHERE column_name IS NULL -- value is missing
WHERE column_name IS NOT NULL -- value is present
Why = NULL Returns Nothing
It is tempting to write manager_id = NULL. The query runs without an error, but it never finds a row:
SELECT COUNT(*) AS rows_found
FROM employees
WHERE manager_id = NULL;
▶ Run this query in the SQL Playground
| rows_found |
|---|
| 0 |
The count is 0. Comparing anything with NULL gives the result unknown, not true, and WHERE only keeps rows where the condition is true. This applies to =, <>, < and > alike.
IS NOT NULL and Counting
An order that has not shipped yet has NULL in shipped_date. COUNT(*) counts rows, while COUNT(column) counts only the rows where that column is not NULL, so one query shows both groups:
SELECT COUNT(*) AS all_orders,
COUNT(shipped_date) AS shipped,
COUNT(*) - COUNT(shipped_date) AS not_shipped
FROM orders;
▶ Run this query in the SQL Playground
| all_orders | shipped | not_shipped |
|---|---|---|
| 200 | 103 | 97 |
To list the shipped orders themselves, filter with IS NOT NULL:
SELECT id, status, shipped_date
FROM orders
WHERE shipped_date IS NOT NULL
ORDER BY id
LIMIT 5;
▶ Run this query in the SQL Playground
| id | status | shipped_date |
|---|---|---|
| 2 | delivered | 2023-04-09 |
| 3 | delivered | 2023-07-10 |
| 5 | delivered | 2023-12-21 |
| 6 | shipped | 2024-12-13 |
| 8 | delivered | 2023-08-22 |
How NULL Hides Rows From Other Filters
Because a comparison with NULL is never true, rows with NULL drop out of filters that look as if they should cover everything. Split the employees by whether their manager is employee 3:
SELECT SUM(CASE WHEN manager_id = 3 THEN 1 ELSE 0 END) AS manager_is_3,
SUM(CASE WHEN manager_id <> 3 THEN 1 ELSE 0 END) AS manager_is_not_3,
SUM(CASE WHEN manager_id IS NULL THEN 1 ELSE 0 END) AS manager_is_null,
COUNT(*) AS all_employees
FROM employees;
▶ Run this query in the SQL Playground
| manager_is_3 | manager_is_not_3 | manager_is_null | all_employees |
|---|---|---|---|
| 11 | 99 | 10 | 120 |
"Equal to 3" and "not equal to 3" together do not add up to 120. The 10 employees with no manager belong to neither group. To include them, say so: WHERE manager_id <> 3 OR manager_id IS NULL.
Finding Rows With No Match
IS NULL is also how you find rows that have no partner in another table. A LEFT JOIN keeps every customer and fills the order columns with NULL when there is no order; filtering on that NULL leaves the customers who have never ordered:
SELECT u.id, u.first_name, u.last_name
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.id IS NULL
ORDER BY u.id
LIMIT 5;
▶ Run this query in the SQL Playground
| id | first_name | last_name |
|---|---|---|
| 3 | Kenneth | Young |
| 9 | Sandra | Jackson |
| 12 | Richard | Thomas |
| 14 | Jeffrey | Lopez |
| 17 | Stephanie | Mitchell |
Where NULLs Appear When You Sort
| Database | ORDER BY column ASC puts NULLs |
|---|---|
| SQLite, MySQL, SQL Server | First |
| PostgreSQL, Oracle | Last |
PostgreSQL, Oracle and SQLite let you choose with ORDER BY column NULLS FIRST or NULLS LAST. To show a replacement value instead of NULL, use COALESCE.
Common NULL Mistakes
Three errors that produce wrong results without any error message.
1. Comparing with = NULL
SELECT *
FROM employees
WHERE manager_id = NULL;
SELECT *
FROM employees
WHERE manager_id IS NULL;
Any comparison with NULL is unknown, so the first query always returns zero rows.
2. Forgetting the NULL rows in a "not equal" filter
SELECT *
FROM employees
WHERE manager_id <> 3;
SELECT *
FROM employees
WHERE manager_id <> 3
OR manager_id IS NULL;
Rows where the column is NULL are neither equal nor unequal to 3, so they are left out unless you add them back.
3. Using COUNT(column) to count rows
SELECT COUNT(shipped_date)
FROM orders;
SELECT COUNT(*)
FROM orders;
COUNT(column) ignores rows where the column is NULL. Use COUNT(*) for the number of rows, and COUNT(column) only when you mean "rows that have a value".
Practice IS NULL Queries
4 exercises on the real Playground tables.
Write each query yourself first. Every solution has been run against the Playground database.
-
1Easy How many orders have not been shipped yet? Table:
ordersShow solution
SELECT COUNT(*) AS not_shipped FROM orders WHERE shipped_date IS NULL;▶ Run this query in the SQL Playground
IS NULL keeps the orders with no shipped date.
-
2Easy List the departments of employees who have no manager, with the number of such employees in each. Table:
employeesShow solution
SELECT department, COUNT(*) AS employees FROM employees WHERE manager_id IS NULL GROUP BY department ORDER BY employees DESC, department;▶ Run this query in the SQL Playground
Filter with IS NULL first, then group the remaining rows by department.
-
3Medium For each order status, show the number of orders and how many of them have a shipped date. Table:
ordersShow solution
SELECT status, COUNT(*) AS orders, COUNT(shipped_date) AS with_shipped_date FROM orders GROUP BY status ORDER BY orders DESC;▶ Run this query in the SQL Playground
COUNT(*) counts all orders in the group; COUNT(shipped_date) counts only those where the date is present.
-
4Medium Find five products that have no reviews. Table:
products, reviewsShow solution
SELECT p.id, p.name FROM products p LEFT JOIN reviews r ON r.product_id = p.id WHERE r.id IS NULL ORDER BY p.id LIMIT 5;▶ Run this query in the SQL Playground
The LEFT JOIN keeps every product; rows where the review columns are NULL are products without a review.
📝 Quiz — IS NULL
Test your understanding · 0/5 answered
How do you find rows where the column email has no value?
IS NULL is the only reliable test for a missing value. = NULL never matches.
What does "WHERE price = NULL" return?
A comparison with NULL is unknown rather than true, so no row passes the filter.
A table has 100 rows and 20 of them have NULL in discount. What does COUNT(discount) return?
COUNT(column) counts only the rows where the column is not NULL: 100 - 20 = 80.
Which statement about NULL is true?
NULL is a marker for a missing value. It is not equal to zero, to an empty string or to another NULL.
Which filter returns rows where status is not "paid", including rows where status is NULL?
status <> 'paid' is unknown for NULL rows, so they must be added back with OR status IS NULL.
IS NULL Interview Questions
Q1. What is the difference between NULL, zero and an empty string?
Zero and an empty string are known values. NULL means no value has been stored. They behave differently in comparisons, in aggregates and in constraints such as NOT NULL.
Q2. Why does NULL = NULL not return true?
SQL uses three-valued logic: true, false and unknown. Comparing two unknown values cannot be confirmed as equal, so the result is unknown, and WHERE treats unknown as "do not return the row".
Q3. How do aggregate functions treat NULL?
COUNT(*) counts every row. COUNT(column), SUM, AVG, MIN and MAX ignore NULLs. An average is therefore taken over the rows that have a value, not over all rows.
Frequently Asked Questions
How do I check for NULL in SQL?
Use IS NULL to find rows where a column has no value and IS NOT NULL to find rows where it has one, for example WHERE phone IS NULL.
Why does = NULL not work in SQL?
NULL represents an unknown value, and comparing anything with an unknown value gives the result unknown rather than true. WHERE keeps only rows where the condition is true, so = NULL returns no rows.
Is NULL the same as zero or an empty string?
No. Zero and an empty string are real values. NULL means that no value has been stored at all. Oracle is an exception in one respect: it treats an empty string as NULL.
How do I replace NULL with another value?
Use COALESCE, which returns the first non-NULL value in its list, for example COALESCE(phone, 'not provided').