NULL in SQL: why your WHERE clause silently drops rows
NULL means “unknown”, not “empty”. Comparisons with an unknown value are neither true nor false — they are unknown, and WHERE only keeps rows that are definitely true. That one detail causes a surprising number of missing rows.
= NULL never matches
status = NULL returns unknown for every row, so the query returns nothing. Use IS NULL and IS NOT NULL — they are the only operators that test for it.
-- returns no rows
WHERE status = NULL
-- correct
WHERE status IS NULLNOT IN with a NULL in the list returns nothing
If the subquery behind NOT IN can produce a NULL, the whole condition becomes unknown and you get an empty result. NOT EXISTS is immune, which is why it is the safer default.
-- risky
WHERE id NOT IN (SELECT customer_id FROM orders)
-- safe
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = customers.id
)Aggregates skip NULL — except COUNT(*)
AVG(score) ignores NULL scores, so the average is over the rows that have a value, not over all rows. If you want missing scores treated as zero, say so with COALESCE. SUM() of an all-NULL set returns NULL, not 0 — wrap it if a number is required.
SELECT AVG(COALESCE(score, 0)) AS avg_including_missing,
COALESCE(SUM(total), 0) AS total_spent
FROM results;Displaying a fallback
COALESCE(value, fallback) is standard everywhere. MySQL also has IFNULL, SQL Server has ISNULL — COALESCE is the portable choice, and it accepts more than two arguments so you can chain fallbacks.
Finally, remember that a NULL and an empty string are different values. If your data mixes both, filter with (col IS NULL OR col = '') or clean the column up.
In short: Test NULL with IS NULL, prefer NOT EXISTS over NOT IN, and use COALESCE whenever a missing value should read as zero or a default.
Try it on your own tables
Paste your schema, ask in plain English, and SQLGenie writes the query and explains it. It also fixes broken SQL and suggests faster versions. Free, no account, nothing stored.