Handle NULLs in SQL
NULL is not a value — it is the absence of one
A NULL marks a slot where a value is missing or unknown. It is not zero, not an empty string, and not the boolean false. Two unknown things are not knowably equal, so SQL never reports that one NULL equals another. This single idea — "unknown, not a value" — drives every surprising behaviour on this page.
A column is declared to permit or forbid NULLs at definition time. NOT NULL forbids the gap; the default permits it:
CREATE TABLE students (
id INT,
email VARCHAR(255) NULL, -- gap allowed
name VARCHAR(255) NOT NULL -- gap forbidden
);To test the slot itself you must use the dedicated predicates IS NULL / IS NOT NULL — never = NULL, which can only ever be unknown and so matches nothing:
SELECT * FROM students WHERE email IS NULL;Three-valued logic: TRUE, FALSE, and UNKNOWN
Because NULL means "unknown", any comparison that touches a NULL yields a third truth value, UNKNOWN, rather than TRUE or FALSE. SQL's WHERE, ON, and HAVING clauses are three-valued: a row or group is kept only when its predicate evaluates to TRUE. Both FALSE and UNKNOWN cause it to be dropped.
The truth tables for the connectives follow Kleene's strong three-valued logic. Note the two cases where a NULL operand does not poison the result: TRUE OR UNKNOWN is TRUE (one true disjunct is enough), and FALSE AND UNKNOWN is FALSE (one false conjunct is enough).
| AND | T | F | U |
|---|---|---|---|
| T | T | F | U |
| F | F | F | F |
| U | U | F | U |
| OR | T | F | U |
|---|---|---|---|
| T | T | T | T |
| F | T | F | U |
| U | T | U | U |
NULLs in aggregates, GROUP BY, and ORDER BY
Aggregate functions skip NULLs — with one deliberate exception. COUNT(*) counts rows regardless of content, but COUNT(col), SUM, AVG, MIN, and MAX all ignore NULL inputs. This is why AVG(salary) divides by the number of non-NULL salaries, not by the row count — a frequent source of "wrong average" bugs.
-- 5 rows, 2 of them NULL bonus
SELECT COUNT(*) AS rows, -- 5
COUNT(bonus) AS with_bonus,-- 3
SUM(bonus) AS total, -- sum of the 3 non-NULLs
AVG(bonus) AS mean -- total / 3, NOT total / 5
FROM employees;For grouping and sorting, SQL treats all NULLs as a single "unknown" bucket even though they are not equal under comparison: GROUP BY collapses every NULL into one group, and DISTINCT keeps just one NULL. Ordering is engine-specific — PostgreSQL and Oracle sort NULLs last by default, MySQL and SQL Server sort them first — so spell it out with ORDER BY col NULLS LAST (or NULLS FIRST) where the dialect supports it.
Replacing NULLs: COALESCE, NULLIF, and friends
The portable tool is COALESCE(a, b, c, …): it returns the first non-NULL argument, left to right. Use it to supply a default at read time:
SELECT name, COALESCE(phone, mobile, 'no contact') AS reach
FROM customers;COALESCE— ANSI standard, variadic, works everywhere. Prefer it.NULLIF(a, b)— returns NULL whena = b, elsea. The classic use is guarding division:x / NULLIF(y, 0)yields NULL instead of a divide-by-zero error.IFNULL(a, b)(MySQL) andISNULL(a, b)(SQL Server) — two-argument, dialect-specific shortcuts forCOALESCE. Oracle's historical equivalent isNVL(a, b).
Going the other way, an arithmetic expression with a NULL operand is itself NULL: 10 + NULL is NULL, and salary * NULL is NULL. Substitute before computing — COALESCE(salary, 0) * 1.1 — when a missing value should behave like zero.
Common pitfalls
NOT INwith a NULL in the list returns no rows.x NOT IN (1, 2, NULL)expands tox <> 1 AND x <> 2 AND x <> NULL; the last conjunct is UNKNOWN, so the whole predicate can never be TRUE. PreferNOT EXISTSor filter the NULLs out of the subquery.- String concatenation can blank the whole string — but the behaviour is dialect-specific. In PostgreSQL,
'Hi ' || lastnameyields NULL whenlastnameis NULL, and in SQL Server,'Hi ' + lastnamedoes the same whenCONCAT_NULL_YIELDS_NULLis ON (the default). Oracle is the textbook exception: its||operator treats a NULL operand as an empty string, so'Hi ' || NULLreturns'Hi ', not NULL — Oracle famously equates the empty string with NULL. The portable fix is theCONCAT(...)function, which treats NULL arguments as empty strings on every engine. - Equality never matches NULL.
WHERE col = NULLandWHERE col <> NULLboth evaluate to UNKNOWN and return nothing. UseIS NULL/IS NOT NULL, or the null-safe equality<=>in MySQL andIS NOT DISTINCT FROMin PostgreSQL. - A
CHECKconstraint does not reject NULLs. Because CHECK is satisfied on TRUE or UNKNOWN, a row with a NULL in the checked column passes. If the column must also be present, addNOT NULLalongside the CHECK.
Source
Adapted and expanded from the "Handle NULLs in SQL" lesson in the SQL Fundamentals track, cross-checked against the ISO/IEC 9075 SQL standard's three-valued logic and the official documentation for PostgreSQL, MySQL, Oracle Database, and SQL Server (T-SQL) for the dialect-specific concatenation, ordering, and null-handling behaviours.
🤖 Don't fully get this? Learn it with Claude
Stuck on Handle NULLs in SQL? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **Handle NULLs in SQL** (Databases) and want to truly understand it. Explain Handle NULLs in SQL from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Socratic — adapts to where you're stuck.
Teach me **Handle NULLs in SQL** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Active recall exposes what you missed.
Quiz me on **Handle NULLs in SQL** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Intuition + hook + flashcards for long-term memory.
Help me remember **Handle NULLs in SQL** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.