SQL NULL: handling missing values
In a table, some information can be missing: an age nobody entered, an employee without a manager… SQL represents these empty cells with a special value, NULL. It is one of the notions that trips up beginners the most, because NULL does not compare like a normal value.
Syntax
WHERE column IS NULL
WHERE column IS NOT NULL
COALESCE(column, default_value)How it works
NULL means “unknown”
NULL is neither 0 nor an empty string: it is the absence of a value. Comparing something to an unknown value gives an unknown result. That is why age = NULL is never true, even when the age is missing.
To test for a missing value, there are two dedicated operators: IS NULL and IS NOT NULL.
NULL in calculations
- A calculation with NULL gives NULL:
NULL + 5is NULL. - The aggregate functions
SUM,AVG,MIN,MAXandCOUNT(column)ignore NULLs.COUNT(*), however, counts every row.
Replacing NULL with a value
COALESCE(a, b, c) returns the first non-NULL value of the list. COALESCE(age, 0) shows 0 instead of a missing age. SQLite also offers IFNULL(a, b).
Diagram
| prenom | age |
|---|---|
| Léa | 34 |
| Hugo | NULL |
| Inès | 27 |
| Adam | NULL |
| prenom | age |
|---|---|
| Hugo | NULL |
| Adam | NULL |
Simplified diagram to show the idea. The examples below use the real SQL Quest practice database (a fictional online shop: see the full schema).
Glossary: the sample database uses French names
clients- customers
commandes- orders
lignes_commande- order lines
produits- products
fournisseurs- suppliers
employes- employees
prenom / nom- first name / last name (or name)
ville- city
pays- country
prix- price
prix_unitaire- unit price
quantite- quantity
categorie- category
date_commande- order date
poste- job title
departement- department
salaire- salary
statut- status: livrée = delivered, expédiée = shipped, en cours = in progress, annulée = cancelled
Examples with their results
1. Find missing values
The customers whose age is not filled in.
SELECT prenom, nom, age
FROM clients
WHERE age IS NULL; Try it in the sandbox| prenom | nom | age |
|---|---|---|
| Clara | Faure | NULL |
| Lucas | Blanc | NULL |
| Karim | Fontaine | NULL |
| Clara | Lefebvre | NULL |
4 rows in total
2. Replace NULL in the output
COALESCE replaces the missing age with some text.
SELECT prenom, nom, COALESCE(age, 'not provided') AS age
FROM clients
WHERE age IS NULL OR id <= 3; Try it in the sandbox| prenom | nom | age |
|---|---|---|
| Samir | Dubois | 59 |
| Samir | Martin | 25 |
| Zoé | Laurent | 36 |
| Clara | Faure | not provided |
| Lucas | Blanc | not provided |
| Karim | Fontaine | not provided |
| Clara | Lefebvre | not provided |
7 rows in total
3. COUNT(*) versus COUNT(column)
COUNT(*) counts every row, COUNT(age) only those where the age is known.
SELECT COUNT(*) AS clients, COUNT(age) AS ages_connus
FROM clients; Try it in the sandbox| clients | ages_connus |
|---|---|
| 120 | 116 |
1 row in total
4. Employees without a manager
The CEO has no manager: their manager_id column is NULL.
SELECT prenom, nom, poste
FROM employes
WHERE manager_id IS NULL; Try it in the sandbox| prenom | nom | poste |
|---|---|---|
| Chloé | Dubois | PDG |
1 row in total
Common mistakes
Writing = NULL
The condition = NULL is never true: the query returns no rows, without any error message.
SELECT * FROM clients WHERE age = NULL;SELECT * FROM clients WHERE age IS NULL;Forgetting NULLs with <>
age <> 30 also discards unknown ages, because comparing NULL is never true. Add OR age IS NULL if you want to keep them.
SELECT COUNT(*) FROM clients WHERE age <> 30;SELECT COUNT(*) FROM clients WHERE age <> 30 OR age IS NULL;Confusing NULL with an empty string
An empty string '' is a real value (text of length 0). IS NULL does not find it.
SELECT * FROM clients WHERE email = NULL;SELECT * FROM clients WHERE email IS NULL OR email = '';Exercises
Try writing the query yourself before opening the solution.
Challenge 1. Count the customers whose age is filled in.
Show a solution
SELECT COUNT(*) FROM clients WHERE age IS NOT NULL;| COUNT(*) |
|---|
| 116 |
1 row in total
Try it in the sandboxChallenge 2. Show the first name of each employee and their manager_id, replacing the missing manager_id values with 0.
Show a solution
SELECT prenom, COALESCE(manager_id, 0) AS manager FROM employes;| prenom | manager |
|---|---|
| Chloé | 0 |
| Jade | 1 |
| Nathan | 1 |
| Samir | 1 |
| Alice | 2 |
| Nathan | 2 |
| Lucas | 2 |
| Emma | 2 |
25 rows in total · 8 shown
Try it in the sandboxThe interactive exercises are checked automatically, with a tutor who explains your mistakes. A short lesson stays next to the editor.
Frequently asked questions
Why does WHERE column = NULL return nothing?
Because NULL means “unknown”: comparing a value to something unknown gives neither true nor false, but unknown, and WHERE only keeps true conditions. You have to write IS NULL.
What is the difference between COALESCE and IFNULL?
IFNULL(a, b) takes two values and exists in SQLite and MySQL. COALESCE takes as many values as you like and is part of standard SQL: it works everywhere.
Does AVG take NULLs into account?
No, AVG ignores NULLs: it averages only the known values. To count NULLs as 0, write AVG(COALESCE(column, 0)).