Skip to content
EL
Contents

SQL basics: reading data

NULL and three-valued logic

ConceptFoundation3 min read10 min hands-on#fundamentals#sql

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';   -- 2

How many does this return?

SELECT count(*) FROM customers WHERE city <> 'Pune';

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:

ExpressionResultWhy
true AND unknownunknownDepends on the unknown part
false AND unknownfalseFalse whatever the unknown is
true OR unknowntrueTrue whatever the unknown is
false OR unknownunknownDepends on the unknown part
NOT unknownunknown

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

  1. 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 WHERE keeps only rows where the condition is true.

  2. What does NULL = NULL return, and how do you test whether a value is NULL?

    Show answer

    It returns NULL (unknown). Test with IS NULL or IS NOT NULL, which always return true or false.

  3. Write a condition for “customers not in Pune, including those whose city is unknown”.

    Show answer

    city IS DISTINCT FROM 'Pune', or equivalently city <> 'Pune' OR city IS NULL.

Finished reading?

Saved in this browser only.

Sources

Where each claim on this page comes from.

  1. 1.
    PostgreSQL documentation: 9.2 Comparison Functions and Operators
    Primary· §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. 2.
    PostgreSQL documentation: 9.1 Logical Operators
    Primary· §9.1 Logical Operators

    SQL uses three-valued logic with true, false and null, and the truth tables for AND, OR and NOT.

  3. 3.
    PostgreSQL documentation: SELECT
    Primary· WHERE Clause

    A row satisfies WHERE only if the condition returns true.

  4. 4.
    PostgreSQL documentation: 9.18 Conditional Expressions
    Primary· §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. 1.
    PostgreSQL documentation: 9.1 Logical Operators
    Primary· §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.