SQLab Hub
Lesson 38 · IS NULL
Lesson 38 · More Topics

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.

∅ IS NULL · IS NOT NULL ▶ Runnable Playground examples 🧪 4 practice exercises 📝 5-question quiz ⏱ ~15 min

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:

SQLite · runs in the Playground
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

Employees with no manager
first_namelast_nametitle
StevenBakerParalegal
MichaelCampbellSenior Counsel
WilliamLewisSenior CS Manager
JohnMillerParalegal
MichelleMillerCOO
JasonMitchellProduct Designer
✓ 10 rows · first 6 shown · real output from the Playground database

10 of the 120 employees have no manager recorded.

SQL · syntax
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:

SQLite · runs in the Playground
SELECT COUNT(*) AS rows_found
FROM employees
WHERE manager_id = NULL;

▶ Run this query in the SQL Playground

Rows matched by = NULL
rows_found
0
✓ 1 row · real output from the Playground database

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:

SQLite · runs in the Playground
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

Orders with and without a shipped date
all_ordersshippednot_shipped
20010397
✓ 1 row · real output from the Playground database

To list the shipped orders themselves, filter with IS NOT NULL:

SQLite · runs in the Playground
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

Orders that have a shipped date
idstatusshipped_date
2delivered2023-04-09
3delivered2023-07-10
5delivered2023-12-21
6shipped2024-12-13
8delivered2023-08-22
✓ 5 rows · real output from the Playground database

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:

SQLite · runs in the Playground
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

Employees split by manager, including the NULL group
manager_is_3manager_is_not_3manager_is_nullall_employees
119910120
✓ 1 row · real output from the Playground database

"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.

NOT IN has the same trap. If the list or subquery after NOT IN contains a NULL, the condition is never true and the query returns no rows. Filter the NULLs out of the subquery, or use NOT EXISTS.

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:

SQLite · runs in the Playground
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

Customers with no orders
idfirst_namelast_name
3KennethYoung
9SandraJackson
12RichardThomas
14JeffreyLopez
17StephanieMitchell
✓ 5 rows · real output from the Playground database

Where NULLs Appear When You Sort

DatabaseORDER BY column ASC puts NULLs
SQLite, MySQL, SQL ServerFirst
PostgreSQL, OracleLast

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

❌ Never matches
SQL
SELECT *
FROM employees
WHERE manager_id = NULL;
✅ Use IS NULL
SQL
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

❌ Drops rows with NULL
SQL
SELECT *
FROM employees
WHERE manager_id <> 3;
✅ Includes them
SQL
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

❌ Skips NULLs
SQL
SELECT COUNT(shipped_date)
FROM orders;
✅ Counts every row
SQL
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.

  1. 1Easy How many orders have not been shipped yet? Table: orders

    Show solution
    SQLite · runs in the Playground
    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.

  2. 2Easy List the departments of employees who have no manager, with the number of such employees in each. Table: employees

    Show solution
    SQLite · runs in the Playground
    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.

  3. 3Medium For each order status, show the number of orders and how many of them have a shipped date. Table: orders

    Show solution
    SQLite · runs in the Playground
    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.

  4. 4Medium Find five products that have no reviews. Table: products, reviews

    Show solution
    SQLite · runs in the Playground
    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

Question 1 of 5

How do you find rows where the column email has no value?

Question 2 of 5

What does "WHERE price = NULL" return?

Question 3 of 5

A table has 100 rows and 20 of them have NULL in discount. What does COUNT(discount) return?

Question 4 of 5

Which statement about NULL is true?

Question 5 of 5

Which filter returns rows where status is not "paid", including rows where 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').