Real data is rarely complete. A customer skips the optional phone number, a product hasn't been given a weight yet, an order hasn't been shipped so it has no delivery date. Databases mark these gaps with a special marker called NULL.
NULL doesn't mean zero, and it doesn't mean an empty piece of text. It means "unknown" or "missing". That small idea has big consequences: a comparison with an unknown value can't be true or false, so it's unknown too. Many confusing query results, such as rows that seem to vanish or totals that don't add up, come from NULL.
In this lesson you'll learn how NULL behaves in comparisons, calculations and counts, how to test for it properly, how to replace it with a sensible default, and one trap with NOT IN that catches even experienced developers.
Meet NULL
For this lesson, the customers table has an extra email column. Chloe and Ema haven't given one:
| id | name | city | |
|---|---|---|---|
| 1 | Ana | Lisbon | ana@example.com |
| 2 | Ben | Leeds | ben@example.com |
| 3 | Chloe | Lyon | NULL |
| 4 | Dev | Leeds | dev@example.com |
| 5 | Ema | Porto | NULL |
NULL is different from '' (an empty string) and from 0. An empty string means "we know there's no text"; NULL means "we don't know". Columns marked NOT NULL when the table is created can never hold it, as you'll see in the constraints lesson.
Testing for NULL
Because NULL is unknown, email = NULL doesn't give true or false. It gives NULL, and WHERE only keeps rows where the condition is true. So this query returns nothing, ever:
SELECT name FROM customers WHERE email = NULL; -- always 0 rowsUse the special operators instead:
SELECT name FROM customers WHERE email IS NULL; -- Chloe, Ema
SELECT name FROM customers WHERE email IS NOT NULL; -- Ana, Ben, DevThe same rule hides rows in other conditions. WHERE email <> 'ben@example.com' returns only Ana and Dev, because for Chloe and Ema the answer is unknown, not true.
NULL in calculations and logic
Almost any calculation involving NULL gives NULL: 5 + NULL is NULL, and so is 'Hi ' || NULL. SQL uses three-valued logic: true, false and unknown.
| Expression | Result |
|---|---|
NULL = NULL | NULL (unknown) |
NULL OR TRUE | TRUE |
NULL AND FALSE | FALSE |
NULL AND TRUE | NULL |
When one side already decides the answer, as with OR TRUE or AND FALSE, the unknown doesn't matter. Otherwise, the result stays unknown.
COALESCE, counts and sorting
COALESCE(a, b, ...) returns the first value that isn't NULL, which is the standard way to show a default: COALESCE(email, 'not given').
Aggregate functions skip NULLs. COUNT(*) counts rows, but COUNT(email) counts only rows where email has a value. SUM and AVG ignore NULLs too, so an average is taken over the known values only.
When sorting, SQLite and MySQL put NULLs first in ascending order, while PostgreSQL puts them last. PostgreSQL and SQLite let you choose with ORDER BY email NULLS LAST.
SQL NULL Syntax
NULL handling
WHERE column IS NULL
WHERE column IS NOT NULL
COALESCE(column, 'default')
COUNT(*) -- every row
COUNT(column) -- rows where column isn't NULL
ORDER BY column NULLS LAST -- PostgreSQL, SQLiteSQL NULL Summary
- NULL means unknown or missing; it isn't 0 or an empty string.
- Test with
IS NULLandIS NOT NULL;= NULLnever matches anything. - Comparisons and calculations with NULL usually give NULL, and WHERE drops those rows.
COUNT(column),SUMandAVGskip NULLs;COUNT(*)counts every row.COALESCE(value, default)replaces NULL with a value of your choice.
SQL NULL Examples
Each example shows the code, the output it produces and what each part does.
Showing a default with COALESCE
SELECT name,
COALESCE(email, 'not given') AS email
FROM customers;name | email
------+----------------
Ana | ana@example.com
Ben | ben@example.com
Chloe | not given
Dev | dev@example.com
Ema | not givenLine-by-line explanation
- Line 2Where email is NULL, COALESCE uses the second value instead. The result column keeps the name email thanks to AS.
Counting missing values
SELECT COUNT(*) AS customers,
COUNT(email) AS with_email,
COUNT(*) - COUNT(email) AS missing
FROM customers;customers | with_email | missing
----------+------------+--------
5 | 3 | 2Line-by-line explanation
- Line 1COUNT(*) counts all five rows.
- Line 2COUNT(email) skips the two NULLs.
- Line 3The difference is the number of customers with no email, a quick data-quality check.
Why = NULL doesn't work
SELECT name,
email = NULL AS equals_null,
email IS NULL AS is_null
FROM customers;name | equals_null | is_null
------+-------------+--------
Ana | NULL | 0
Ben | NULL | 0
Chloe | NULL | 1
Dev | NULL | 0
Ema | NULL | 1Line-by-line explanation
- Line 2Comparing anything with NULL using = gives NULL for every row, even where email really is missing.
- Line 3IS NULL gives a proper answer: 1 means true and 0 means false in SQLite and MySQL.
Common SQL NULL Mistakes
Using = NULL
SELECT * FROM customers WHERE email = NULL;SELECT * FROM customers WHERE email IS NULL;Forgetting NULLs in a "not equal" filter
-- "everyone except Ben" but Chloe and Ema disappear
SELECT name FROM customers WHERE email <> 'ben@example.com';SELECT name FROM customers
WHERE email <> 'ben@example.com' OR email IS NULL;NOT IN with a list that contains NULL
-- returns no rows at all if any manager_id is NULL
SELECT name FROM staff
WHERE id NOT IN (SELECT manager_id FROM staff);SELECT name FROM staff
WHERE id NOT IN (SELECT manager_id FROM staff WHERE manager_id IS NOT NULL);x NOT IN (1, NULL) means x <> 1 AND x <> NULL, and the second part is always unknown.
SQL NULL in Real Projects
NULL in real data
Analysts check for NULLs first when they receive a new dataset, because missing values distort averages and counts. Reports use COALESCE to show "n/a" or 0 instead of blank cells. Shipping systems use NULL delivery dates to find orders that haven't left the warehouse, with a simple WHERE shipped_at IS NULL.
SQL NULL FAQ
What is the difference between COALESCE, IFNULL and ISNULL?
COALESCE(a, b, c) is standard SQL and returns the first non-NULL value from any number of arguments, so it works in every major database. IFNULL in MySQL and SQLite, ISNULL in SQL Server and NVL in Oracle do the same job for exactly two values. Prefer COALESCE for portable queries.
Does COUNT include NULL values?
COUNT(*) counts every row, including rows with NULLs. COUNT(column) counts only rows where that column isn't NULL. So on a table of five customers where two have no email, COUNT(*) is 5 and COUNT(email) is 3. SUM, AVG, MIN and MAX also ignore NULLs.
Where do NULLs appear when sorting?
It depends on the database. PostgreSQL puts NULLs last in ascending order and first in descending order; MySQL and SQLite put them first in ascending order. PostgreSQL and SQLite let you choose with ORDER BY email NULLS LAST; elsewhere, sort by email IS NULL first.
Can a column with a UNIQUE constraint contain several NULLs?
In PostgreSQL, MySQL and SQLite, yes, because NULL isn't considered equal to another NULL, so several rows can have a missing value. SQL Server allows only one NULL in a unique column unless you use a filtered index. A primary key column can never be NULL.
Should I store missing values as NULL, an empty string or zero?
Use NULL when the value is unknown or not applicable, such as an email nobody has provided. Use 0 or '' only when they're real values: a stock level of zero is known, not missing. Mixing them makes counts and averages misleading, because 0 is included in AVG and NULL isn't.
SQL NULL Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the query to find customers with no email address.
SELECT name FROM customers WHERE email ;
What does NULL mean in a database?
A column bonus holds 100, NULL and 200. What does SELECT COUNT(bonus) FROM staff; return?
What does SELECT COALESCE(NULL, NULL, 'x', 'y'); return?
Using the same bonus values (100, NULL, 200), what does AVG(bonus) return?
SQL NULL Quiz
5 questions to check what you learned.
Which condition finds rows where
phonehas a value?- phone != NULL
- phone <> ''
- phone IS NOT NULL
- NOT phone = NULL
What is
10 + NULL?- NULL
- 10
- 0
- An error
What's the difference between
COUNT(*)andCOUNT(email)?- None
- COUNT(*) counts all rows; COUNT(email) counts rows where email isn't NULL
- COUNT(email) counts unique emails
- COUNT(*) skips NULLs
What does
NULL OR TRUEevaluate to?- NULL
- FALSE
- TRUE
- An error
Why can
WHERE id NOT IN (subquery)return no rows unexpectedly?- Subqueries can't be used with NOT IN
- If the subquery returns a NULL, every NOT IN comparison becomes unknown
- NOT IN only works with numbers
- The subquery runs once per row