MySQL IF: function, statement, and IFNULL
MySQL has two different IFs: an IF() function you use in queries, and an IF statement that only works inside stored programs.
Quick answer
In a query, IF() is a function that takes three arguments:
In a stored procedure or function, IF is a statement:
These are different things and they are not interchangeable. Using the statement form in a plain query is the most common mistake here.
The IF() function
All three arguments are required. There is no two-argument form, and unlike CASE it cannot chain.
Truthiness rules
MySQL treats the condition as a number, and this catches people out:
Two things worth pinning down:
IF(NULL, ...) takes the false branch. A NULL condition is not true, so it falls through. That is reasonable, but it means IF(x, 'yes', 'no') returns 'no' both when x is false and when x is unknown, silently merging two different states. If you care about the difference, test for it explicitly.
A non-numeric string is false. IF('abc', ...) returns the false branch, because 'abc' converts to 0. So IF(status, ...) on a text column is almost never what you want.
The IF statement
Inside a stored procedure, function, or trigger, IF is control flow:
Note ELSEIF is one word. ELSE IF as two words parses as a nested IF inside an ELSE, which then needs its own END IF. It usually still works, and it reads as a bug.
Try the statement form in a plain query and you get:
A 1064 near an IF almost always means you wanted the function, not the statement.
IFNULL
Two arguments. Returns the first if it is not NULL, otherwise the second:
IFNULL only tests for NULL. It does not treat empty strings or zeros as missing, which is usually right and occasionally surprising:
If empty strings should also fall back, say so:
Or use NULLIF to convert empties to NULL first:
COALESCE
IFNULL with any number of arguments. Returns the first non-NULL:
Alan's status is NULL, so it fell through to his email.
Prefer COALESCE over IFNULL. It is standard SQL and works in PostgreSQL, SQLite, and SQL Server, whereas IFNULL is MySQL-specific. There is no performance difference. IFNULL is shorter, and that is its only advantage.
NULLIF
The inverse. Returns NULL if the two arguments are equal, otherwise the first:
Its main use is guarding division:
Without it, count = 0 gives a division-by-zero. With it, the divisor becomes NULL and the result is NULL, which is usually the honest answer for "average of nothing".
IF() vs CASE
IF() handles one condition. CASE handles many, and is standard SQL:
The equivalent with nested IF() is legal and horrible:
Two branches is the point where CASE wins. It also ports to other databases, which nested IF() does not.
CASE has a shorthand form for equality against one expression:
Careful with that form and NULL: CASE status WHEN NULL THEN ... never matches, because it compares with =, and NULL = NULL is unknown. Use the long form with WHEN status IS NULL THEN.
Without an ELSE, CASE returns NULL when nothing matches. That is a common source of unexpected NULLs.
Using IF() to count and aggregate
This is where IF() earns its keep, and it is genuinely useful:
Both SUM columns produce the same number, because a comparison in MySQL evaluates to 1 or 0. The shorthand is idiomatic MySQL but does not port; the IF() form is clearer to a reader.
For conditional aggregates, CASE inside the aggregate is the portable pattern:
Note the missing ELSE in the AVG. That is deliberate, and it changes the answer. Given two active orders of 100 and 200, and one inactive order:
AVG ignores NULLs, so omitting ELSE averages only the active rows and gives 150. Adding ELSE 0 feeds a zero into the average for every inactive row and drags it to 100. The first is the average order value of active orders. The second is not the average of anything anybody asked for.
IF() in WHERE, and why not to
You can:
You should not. Wrapping a column in a function stops MySQL using an index on it, so this forces a full scan. Write the boolean logic directly:
Longer, and it can use an index.
Common problems
ERROR 1064 near IF. You used the statement form outside a stored program. Use IF() the function, or CASE.
IF() returns the false branch for rows I expected to match. The condition is NULL. IF(NULL, ...) is false. Check for NULLs in the columns you are testing.
IFNULL is not replacing my empty strings. It only tests NULL. Use NULLIF(col, '') first, or test explicitly.
CASE returns NULL unexpectedly. No branch matched and there is no ELSE.
My query got slow after adding IF() to WHERE. It killed the index. Rewrite as plain boolean conditions.
Quick reference
| Task | Syntax |
|---|---|
| Inline conditional | IF(cond, true_val, false_val) |
| Default for NULL | IFNULL(col, 'fallback') |
| First non-NULL of many | COALESCE(a, b, 'fallback') |
| NULL when equal | NULLIF(a, b) |
| Safe division | a / NULLIF(b, 0) |
| Multiple conditions | CASE WHEN ... THEN ... ELSE ... END |
| Count matching rows | SUM(IF(cond, 1, 0)) |
| Conditional average | AVG(CASE WHEN cond THEN col END) |
| Control flow (stored programs) | IF ... THEN ... ELSEIF ... ELSE ... END IF; |