SQL basics: reading data
NULL and three-valued logic
The idea in 30 seconds
NULL means "unknown", not zero and not empty. Comparing anything with an unknown value gives an unknown result, and WHERE only keeps rows whose condition is true. So a condition and its opposite can both skip the same rows. Test for NULL with IS NULL, and decide on purpose what unknown should mean in every query.
Helps to know first: Install PostgreSQL and learn the environment
On this page
NULL means unknown
Two customers in shop have no city: Elena and Hugo. Their city is NULL. That does not mean an empty string or “nowhere”. It means unknown: the city exists, we just do not know it.
That one idea explains all of NULL’s odd behavior. Is an unknown city equal to Pune? Unknown. Is it different from Pune? Also unknown.
PredictPune, and everything else
shop has 8 customers. Two live in Pune:
SELECT count(*) FROM customers WHERE city = 'Pune'; -- 2How many does this return?
SELECT count(*) FROM customers WHERE city <> 'Pune';4, not 6. The two queries together account for 6 of the 8 customers:
SELECT name, city FROM customers WHERE city <> 'Pune'; name | city
------------+----------
Ben Okafor | Lagos
Chen Wei | Shanghai
Farhan Ali | Karachi
Grace Kim | SeoulElena and Hugo are in neither result. For them, city <> 'Pune' is not true and not false: it is unknown, and WHERE keeps only rows where the condition is true.
Three-valued logic
SQL conditions have three possible results: true, false and unknown (written NULL). Any ordinary comparison with a NULL gives unknown:
SELECT NULL = NULL AS eq, NULL IS NULL AS is_null, 7 <> NULL AS ne;
eq | is_null | ne
----+---------+----
| t |
Even NULL = NULL is unknown: two unknown values might or might not be equal. AND, OR and NOT follow from the same idea:
| Expression | Result | Why |
|---|---|---|
true AND unknown | unknown | Depends on the unknown part |
false AND unknown | false | False whatever the unknown is |
true OR unknown | true | True whatever the unknown is |
false OR unknown | unknown | Depends on the unknown part |
NOT unknown | unknown |
Asking about NULL correctly
Use IS NULL and IS NOT NULL. They always return true or false:
SELECT name, city FROM customers WHERE city IS NULL;
name | city
---------------+------
Elena Petrova |
Hugo Martin |
When you want “different from Pune, counting unknown as different”, say so with IS DISTINCT FROM, which treats NULL like an ordinary value:
SELECT name, city FROM customers WHERE city IS DISTINCT FROM 'Pune';
That returns all six non-Pune customers, Elena and Hugo included.
To show a value in place of NULL, use COALESCE, which returns its first argument that is not NULL:
SELECT name, coalesce(city, 'unknown') AS city FROM customers ORDER BY id;
Check yourself
Why does
WHERE city <> 'Pune'skip customers with no city?Show answer
For them the comparison is with NULL, so the result is unknown, and
WHEREkeeps only rows where the condition is true.What does
NULL = NULLreturn, and how do you test whether a value is NULL?Show answer
It returns NULL (unknown). Test with
IS NULLorIS NOT NULL, which always return true or false.Write a condition for “customers not in Pune, including those whose city is unknown”.
Show answer
city IS DISTINCT FROM 'Pune', or equivalentlycity <> 'Pune' OR city IS NULL.
Finished reading?
Saved in this browser only.
Sources
Where each claim on this page comes from.
- 1.PostgreSQL documentation: 9.2 Comparison Functions and OperatorsPrimary· §9.2 Comparison Functions and Operators
Ordinary comparisons with a null input yield null; IS NULL and IS DISTINCT FROM treat null as a comparable value.
- 2.PostgreSQL documentation: 9.1 Logical OperatorsPrimary· §9.1 Logical Operators
SQL uses three-valued logic with true, false and null, and the truth tables for AND, OR and NOT.
- 3.PostgreSQL documentation: SELECTPrimary· WHERE Clause
A row satisfies WHERE only if the condition returns true.
- 4.PostgreSQL documentation: 9.18 Conditional ExpressionsPrimary· §9.18.2 COALESCE
COALESCE returns the first of its arguments that is not null.
Go further
What to read next, and why it is worth the time.
- 1.PostgreSQL documentation: 9.1 Logical OperatorsPrimary· §9.1
Two small truth tables you will want to have seen once. They explain every NULL surprise in SQL.
Applies to PostgreSQL 18. Draft: not yet fact-checked end to end.