VillageSQL is a drop-in replacement for MySQL with extensions.
All examples in this guide work on VillageSQL. Install Now →
NULL Is Not a Value
NULL represents the absence of a value. Comparing NULL to anything — including another NULL — doesn’t return true or false. It returns NULL.WHERE clauses. This means rows with NULL in a filtered column are silently excluded.
IS NULL explicitly:
Testing for NULL
Always useIS NULL and IS NOT NULL, never = NULL or != NULL:
<=> (the NULL-safe equality operator), which treats two NULLs as equal:
WHERE clauses when you want to match NULL-to-NULL, such as in queries comparing two potentially-null columns.
NULL in Aggregates
Aggregate functions (COUNT, SUM, AVG, etc.) ignore NULL values — except COUNT(*).
COUNT(*) returns 3. COUNT(referrer) returns 2 — the NULL row is excluded. This is intentional behavior, but it surprises developers who expect COUNT(column) to equal COUNT(*).
Handling NULL in Expressions
UseCOALESCE to substitute a default value when a column is NULL:
IFNULL(column, default) is equivalent for the two-argument case:
NULLIF(a, b) returns NULL when a = b, otherwise returns a. Useful for avoiding division-by-zero:
| Function | Behavior | Use when |
|---|---|---|
COALESCE(a, b, ...) | Returns the first non-NULL value in the list | You have multiple fallback options |
IFNULL(a, b) | Returns b if a is NULL, otherwise a | Single fallback value; simpler syntax than COALESCE for two arguments |
NULLIF(a, b) | Returns NULL if a = b, otherwise returns a | Suppressing a sentinel value (e.g., avoiding division by zero when count is 0) |
LEFT JOIN returns NULL for right-side columns when no match exists, and filtering on those NULL columns in WHERE silently converts it to an INNER JOIN. See MySQL JOINs Explained for the ON vs. WHERE distinction.
NULL and Indexes
MySQL indexes store NULL values. AWHERE col IS NULL query can use an index on col. However:
NULLvalues are not considered equal in aUNIQUEindex — a unique column can contain multiple NULL values- In a composite index, NULL in any column position is indexed normally
Frequently Asked Questions
Should I use NULL or empty string for optional text fields?
Prefer NULL for genuinely absent data. Empty string ('') means “explicitly set to empty” and is distinct from “not provided.” Mixing them creates inconsistency — WHERE col IS NULL and WHERE col = '' are different conditions. Pick one convention and apply it consistently.
Why does NOT IN behave unexpectedly with NULLs?
NOT IN with a subquery that returns any NULL always returns an empty result set:
NOT EXISTS instead when the subquery might return NULLs:
Does ORDER BY sort NULLs?
Yes. By default, MySQL sorts NULL values first in ascending order and last in descending order. MySQL doesn’t support NULLS FIRST / NULLS LAST syntax (that’s PostgreSQL). To control NULL placement, use a sort expression:
col IS NULL evaluates to 0 for non-NULL values and 1 for NULL, so sorting by it ascending pushes NULLs to the end.
Troubleshooting
| Problem | Solution |
|---|---|
WHERE col != 'value' excludes NULL rows | Add OR col IS NULL to the condition |
COUNT(column) doesn’t match COUNT(*) | Expected — COUNT(column) skips NULLs. Use COUNT(*) for total rows |
NOT IN subquery returns empty result | Subquery returns NULL — use NOT EXISTS instead |
= NULL in WHERE matches nothing | Replace with IS NULL |
| UNIQUE column allows duplicate NULLs | Expected behavior — NULLs are not considered equal in unique indexes |

