SQLab Hub
Lesson 28 · NULLIF
Lesson 28 · Functions

SQL NULLIF: Syntax, Examples and the Divide-by-Zero Fix

NULLIF(a, b) returns NULL when a equals b, and returns a otherwise. Its best-known use is preventing division-by-zero errors: x / NULLIF(y, 0) gives NULL instead of failing when y is 0. It also turns placeholder values such as 0 or 'N/A' into real NULLs.

a = b → NULL Divide-by-zero fix ▶ Runnable Playground examples 🧪 6 practice exercises 📝 30-question quiz ⏱ ~25 min

What Is NULLIF in SQL?

NULLIF compares two values. If they are equal it returns NULL; if they differ it returns the first value unchanged:

SQLite · runs in the Playground
SELECT NULLIF(100, 100)     AS same_numbers,
       NULLIF(100, 50)      AS different_numbers,
       NULLIF('SQL', 'SQL')   AS same_text,
       NULLIF('SQL', 'MySQL') AS different_text;

▶ Run this query in the SQL Playground

NULLIF on literal values
same_numbersdifferent_numberssame_textdifferent_text
NULL100NULLSQL
✓ 1 row · real output from the Playground database
SQL · syntax
NULLIF(value1, value2)

-- means exactly the same as
CASE WHEN value1 = value2 THEN NULL ELSE value1 END
RuleMeaning
EqualReturns NULL
DifferentReturns the first argument
Order mattersNULLIF(a, b) and NULLIF(b, a) return different values when a and b differ
First argument NULLReturns NULL (there is nothing else to return)
Standard SQLWorks the same in PostgreSQL, MySQL, SQL Server, Oracle and SQLite

Preventing Division by Zero

Dividing by zero is an error in PostgreSQL, SQL Server and Oracle, and stops the whole query. Wrapping the divisor in NULLIF(divisor, 0) turns a zero into NULL first. Anything divided by NULL is NULL, so the row gets a NULL instead of the query failing.

A realistic case: the share of successful payments per payment method. If a method had no payments yet, the divisor would be 0:

SQLite · runs in the Playground
SELECT method,
       COUNT(*) AS payments,
       SUM(status = 'success') AS successful,
       ROUND(100.0 * SUM(status = 'success') / NULLIF(COUNT(*), 0), 1) AS success_pct
FROM payments
GROUP BY method
ORDER BY success_pct DESC;

▶ Run this query in the SQL Playground

Success rate per payment method
methodpaymentssuccessfulsuccess_pct
Credit Card141285.7
Google Pay131076.9
PayPal241875
Debit Card282175
Cryptocurrency342470.6
Bank Transfer382360.5
Apple Pay271659.3
Gift Card221359.1
✓ 8 rows · real output from the Playground database

Here no method has zero payments, so NULLIF changes nothing; it is there so the query stays safe when the data changes. The next example shows the case where it matters, using the sample rows below (respondents B and E have zero visits):

SQLite · runs in the Playground
WITH survey(respondent, city, score, visits) AS (
  VALUES ('A', 'Pune',    8, 4),
         ('B', 'N/A',     0, 0),
         ('C', 'Delhi',   9, 3),
         ('D', 'Unknown', 0, 2),
         ('E', 'Pune',    6, 0)
)
SELECT respondent, score, visits,
       ROUND(1.0 * score / NULLIF(visits, 0), 2) AS score_per_visit
FROM survey;

▶ Run this query in the SQL Playground

Score per visit with a zero-safe divisor
respondentscorevisitsscore_per_visit
A842
B00NULL
C933
D020
E60NULL
✓ 5 rows · real output from the Playground database
SQLite and MySQL do not raise the error. In SQLite (the Playground) and MySQL with default settings, 10 / 0 already returns NULL. NULLIF still matters: it makes the query portable to PostgreSQL and SQL Server, and it states the intent. To show 0 instead of NULL, wrap the result in COALESCE: COALESCE(x / NULLIF(y, 0), 0).

Turning Placeholder Values Into NULL

Data often contains values that really mean "unknown": 0 for a missing score, 'N/A' or 'Unknown' for a missing city. Left as they are, they distort counts and averages. NULLIF turns them into NULL, which aggregates skip:

SQLite · runs in the Playground
WITH survey(respondent, city, score, visits) AS (
  VALUES ('A', 'Pune',    8, 4),
         ('B', 'N/A',     0, 0),
         ('C', 'Delhi',   9, 3),
         ('D', 'Unknown', 0, 2),
         ('E', 'Pune',    6, 0)
)
SELECT respondent,
       NULLIF(NULLIF(city, 'N/A'), 'Unknown') AS city,
       NULLIF(score, 0) AS score
FROM survey;

▶ Run this query in the SQL Playground

Placeholder values replaced with NULL
respondentcityscore
APune8
BNULLNULL
CDelhi9
DNULLNULL
EPune6
✓ 5 rows · real output from the Playground database

NULLIF compares with one value, so two placeholders need two nested calls. For a longer list, a CASE with IN ('N/A', 'Unknown', '-') is easier to read.

Effect on averages and counts

SQLite · runs in the Playground
WITH survey(respondent, city, score, visits) AS (
  VALUES ('A', 'Pune',    8, 4),
         ('B', 'N/A',     0, 0),
         ('C', 'Delhi',   9, 3),
         ('D', 'Unknown', 0, 2),
         ('E', 'Pune',    6, 0)
)
SELECT AVG(score)            AS avg_with_zeros,
       ROUND(AVG(NULLIF(score, 0)), 2) AS avg_without_zeros,
       COUNT(NULLIF(city, 'N/A')) AS cities_not_na
FROM survey;

▶ Run this query in the SQL Playground

Averages with and without placeholder zeros
avg_with_zerosavg_without_zeroscities_not_na
4.67.674
✓ 1 row · real output from the Playground database

With the zeros counted as real scores the average is 4.6; treating them as missing gives 7.67. Which one is right depends on what 0 means in your data, which is a question to ask before you clean it.

On real Playground data

7 of the 200 orders have a discount of exactly 0. If you want "the average discount among orders that had one", NULLIF removes them from the average:

SQLite · runs in the Playground
SELECT SUM(discount = 0) AS no_discount,
       ROUND(AVG(discount), 4) AS avg_all_orders,
       ROUND(AVG(NULLIF(discount, 0)), 4) AS avg_discounted_orders
FROM orders;

▶ Run this query in the SQL Playground

Average discount with and without the zero-discount orders
no_discountavg_all_ordersavg_discounted_orders
70.12470.1292
✓ 1 row · real output from the Playground database

NULLIF With COALESCE

NULLIF and COALESCE work in opposite directions: NULLIF turns a value into NULL, COALESCE turns NULL into a value. Combined, they replace one value with another. Here a 0 score becomes "no score" (NULL) and then a label:

SQLite · runs in the Playground
WITH survey(respondent, city, score, visits) AS (
  VALUES ('A', 'Pune',    8, 4),
         ('B', 'N/A',     0, 0),
         ('C', 'Delhi',   9, 3),
         ('D', 'Unknown', 0, 2),
         ('E', 'Pune',    6, 0)
)
SELECT respondent, score,
       COALESCE(CAST(NULLIF(score, 0) AS TEXT), 'not answered') AS score_label,
       COALESCE(1.0 * score / NULLIF(visits, 0), 0) AS score_per_visit_or_0
FROM survey;

▶ Run this query in the SQL Playground

NULLIF and COALESCE together
respondentscorescore_labelscore_per_visit_or_0
A882
B0not answered0
C993
D0not answered0
E660
✓ 5 rows · real output from the Playground database
FunctionTurnsExample
NULLIF(a, b)a matching value into NULLNULLIF(city, 'N/A')
COALESCE(a, b, ...)NULL into the first non-NULL valueCOALESCE(city, 'unknown')
Bothone value into anotherCOALESCE(NULLIF(bonus, 0), 1000)

NULLIF With Text, Dates and CASE

NULLIF works with any type, as long as both arguments are comparable, including text and dates. Here it blanks out the status of orders that are still pending:

SQLite · runs in the Playground
SELECT status,
       NULLIF(status, 'pending') AS status_after_pending,
       COUNT(*) AS orders
FROM orders
GROUP BY status
ORDER BY orders DESC;

▶ Run this query in the SQL Playground

NULLIF on a text column
statusstatus_after_pendingorders
delivereddelivered82
processingprocessing28
cancelledcancelled27
pendingNULL23
shippedshipped21
refundedrefunded19
✓ 6 rows · real output from the Playground database

Because NULLIF only tests equality, any other condition needs CASE WHEN. These two expressions are the same:

SQL · equivalent forms
NULLIF(status, 'pending')
CASE WHEN status = 'pending' THEN NULL ELSE status END

And this one has no NULLIF equivalent, because it tests a range, not equality:

SQL · CASE only
CASE WHEN discount < 0.05 THEN NULL ELSE discount END

NULLIF vs COALESCE, ISNULL, IFNULL and NVL

FunctionDirectionDatabases
NULLIF(a, b)Value → NULL when a = bAll (standard SQL)
COALESCE(a, b, ...)NULL → first non-NULL valueAll (standard SQL)
ISNULL(a, b)NULL → bSQL Server (MySQL's ISNULL(a) only tests for NULL)
IFNULL(a, b)NULL → bMySQL, SQLite
NVL(a, b)NULL → bOracle

The names look alike but NULLIF is the only one that creates NULLs. The others all replace them.

Common NULLIF Mistakes

Five errors and how to avoid them.

1. Swapping the arguments

❌ Returns 0 or NULL, never the value
SQL
SELECT NULLIF(0, discount)
FROM orders;
✅ The value first
SQL
SELECT NULLIF(discount, 0)
FROM orders;

NULLIF returns its first argument when the two differ. With the arguments swapped, every order with a discount gets 0.

2. Wrapping the numerator instead of the divisor

❌ Still divides by zero
SQL
SELECT NULLIF(score, 0) / visits
FROM survey;
✅ Guard the divisor
SQL
SELECT score / NULLIF(visits, 0)
FROM survey;

The error comes from the value you divide by, so that is the one that needs NULLIF.

3. Integer division hiding the result

❌ 9 / 4 gives 2
SQL
SELECT score / NULLIF(visits, 0)
FROM survey;
✅ Force decimal division
SQL
SELECT 1.0 * score / NULLIF(visits, 0)
FROM survey;

In SQLite, PostgreSQL and SQL Server, an integer divided by an integer is an integer. Multiply by 1.0 or CAST to get decimals.

4. Expecting NULLIF to replace NULLs

❌ Leaves NULLs as NULL
SQL
SELECT NULLIF(shipped_date, 'not shipped')
FROM orders;
✅ COALESCE replaces NULL
SQL
SELECT COALESCE(shipped_date, 'not shipped')
FROM orders;

NULLIF only creates NULLs. To show a default for a NULL, use COALESCE.

5. Cleaning away a value that is real

❌ Treats a real 0 as missing
SQL
-- 0 = "scored zero"
SELECT AVG(NULLIF(score, 0)) FROM tests;
✅ Decide what 0 means first
SQL
-- keep real zeros
SELECT AVG(score) FROM tests;

If 0 is a genuine result, removing it inflates the average. NULLIF is for placeholders, not for inconvenient values.

NULLIF Cheat Sheet

TaskPattern
Safe divisionx / NULLIF(y, 0)
Safe division, 0 instead of NULLCOALESCE(x / NULLIF(y, 0), 0)
Decimal result1.0 * x / NULLIF(y, 0)
Placeholder text to NULLNULLIF(city, 'N/A')
Ignore zeros in an averageAVG(NULLIF(score, 0))
Replace one value with anotherCOALESCE(NULLIF(bonus, 0), 1000)
Count values other than XCOUNT(NULLIF(status, 'test'))

Practice NULLIF Queries

6 exercises on the real Playground tables.

Write each query yourself first. Every solution has been run against the Playground database.

  1. 1Easy Show the 8 orders with the smallest discounts, showing NULL instead of 0 for orders without a discount. Table: orders

    Show solution
    SQLite · runs in the Playground
    SELECT id, total, NULLIF(discount, 0) AS discount
    FROM orders
    ORDER BY orders.discount, id
    LIMIT 8;

    ▶ Run this query in the SQL Playground

    NULLIF(discount, 0) returns NULL for the zero-discount orders and the discount for the rest.

  2. 2Easy Count the orders that are not in "pending" status, using NULLIF inside COUNT. Table: orders

    Show solution
    SQLite · runs in the Playground
    SELECT COUNT(NULLIF(status, 'pending')) AS not_pending
    FROM orders;

    ▶ Run this query in the SQL Playground

    Pending rows become NULL and COUNT skips them.

  3. 3Medium Show each product's margin as a percentage of its cost, safely. Show the 6 highest. Table: products

    Show solution
    SQLite · runs in the Playground
    SELECT name, price, cost,
           ROUND(100.0 * (price - cost) / NULLIF(cost, 0), 1) AS markup_pct
    FROM products
    ORDER BY markup_pct DESC
    LIMIT 6;

    ▶ Run this query in the SQL Playground

    No product has a cost of 0 today, but NULLIF keeps the query safe if one is added.

  4. 4Medium For the first 8 customers, show the average order value: total spent divided by the number of orders, with 0 for customers who never ordered. Table: users, orders

    Show solution
    SQLite · runs in the Playground
    SELECT u.id, u.first_name,
           COUNT(o.id) AS orders,
           COALESCE(ROUND(SUM(o.total) / NULLIF(COUNT(o.id), 0), 2), 0) AS avg_order_value
    FROM users u
    LEFT JOIN orders o ON o.user_id = u.id
    GROUP BY u.id, u.first_name
    ORDER BY u.id
    LIMIT 8;

    ▶ Run this query in the SQL Playground

    NULLIF protects the division; COALESCE turns the resulting NULL into 0.

  5. 5Medium Calculate the average helpful votes per review for each rating, ignoring reviews with 0 helpful votes. Table: reviews

    Show solution
    SQLite · runs in the Playground
    SELECT rating,
           COUNT(*) AS reviews,
           ROUND(AVG(NULLIF(helpful_votes, 0)), 1) AS avg_votes_when_voted
    FROM reviews
    GROUP BY rating
    ORDER BY rating DESC;

    ▶ Run this query in the SQL Playground

    AVG skips the NULLs that NULLIF creates for zero votes.

  6. 6Hard Per payment method, show the share of payments that were refunded, guarding the division. Table: payments

    Show solution
    SQLite · runs in the Playground
    SELECT method,
           SUM(status = 'refunded') AS refunded,
           COUNT(*) AS payments,
           ROUND(100.0 * SUM(status = 'refunded') / NULLIF(COUNT(*), 0), 1) AS refund_pct
    FROM payments
    GROUP BY method
    ORDER BY refund_pct DESC;

    ▶ Run this query in the SQL Playground

    SUM of a comparison counts the matching rows in SQLite; elsewhere write SUM(CASE WHEN ... THEN 1 ELSE 0 END).

📝 Quiz — NULLIF

Test your understanding · 0/30 answered

Question 1 of 30

What does NULLIF(value1, value2) do?

Question 2 of 30

What does NULLIF(100, 100) return?

Question 3 of 30

What does NULLIF(100, 50) return?

Question 4 of 30

What does NULLIF('SQL', 'SQL') return?

Question 5 of 30

What does NULLIF('SQL', 'MySQL') return?

Question 6 of 30

How does NULLIF prevent division-by-zero errors?

Question 7 of 30

What does amount / NULLIF(quantity, 0) return when quantity is 0?

Question 8 of 30

Which CASE expression is equivalent to NULLIF(a, b)?

Question 9 of 30

How are NULLIF and COALESCE related?

Question 10 of 30

What does COALESCE(NULLIF(bonus, 0), 1000) return when bonus is 0?

Question 11 of 30

Can NULLIF be used with text?

Question 12 of 30

Can NULLIF be used with dates?

Question 13 of 30

How does AVG(NULLIF(score, 0)) differ from AVG(score)?

Question 14 of 30

What is the common argument-order mistake?

Question 15 of 30

Is NULLIF standard SQL?

Question 16 of 30

What does NULLIF(status, 'cancelled') return for a cancelled order?

Question 17 of 30

What is the difference in flexibility between NULLIF and CASE WHEN?

Question 18 of 30

How does SQL Server's ISNULL differ from NULLIF?

Question 19 of 30

How does IFNULL relate to NULLIF?

Question 20 of 30

Why can NULLIF in WHERE hurt performance?

Question 21 of 30

What does EXPLAIN show for a query that uses NULLIF only in SELECT?

Question 22 of 30

How do you turn the placeholder 'N/A' into a real NULL?

Question 23 of 30

How do you compute a click-through rate safely when impressions can be 0?

Question 24 of 30

Which expression gives a bonus of 1000 when the stored bonus is exactly 0?

Question 25 of 30

Is NULLIF a common interview topic?

Question 26 of 30

How do analysts typically use NULLIF?

Question 27 of 30

What does NULLIF(NULL, NULL) return?

Question 28 of 30

Why is NULLIF often preferred to the equivalent CASE?

Question 29 of 30

What is a risk of NULLIF with mismatched types?

Question 30 of 30

What does COUNT(NULLIF(status, 'test')) count?

NULLIF Interview Questions

Q1. What does NULLIF do?

It returns NULL when its two arguments are equal and the first argument otherwise. It is shorthand for CASE WHEN a = b THEN NULL ELSE a END.

Q2. How do you avoid a division-by-zero error?

Wrap the divisor: x / NULLIF(y, 0). The result is NULL when y is 0. Add COALESCE around it if the report should show 0 instead.

Q3. What is the difference between NULLIF and COALESCE?

They work in opposite directions. NULLIF turns a specific value into NULL; COALESCE turns NULL into the first non-NULL value in its list.

Q4. Why might AVG(NULLIF(x, 0)) be wrong?

Only if 0 is a real value. NULLIF is right when 0 is a placeholder for "unknown"; if 0 is a genuine measurement, removing it overstates the average.

Q5. Does SQLite need NULLIF for division by zero?

SQLite returns NULL for division by zero, as MySQL does by default, so the query does not fail. NULLIF is still good practice because PostgreSQL, SQL Server and Oracle raise an error.

Frequently Asked Questions

What is NULLIF in SQL?

NULLIF is a function that compares two values. If they are equal it returns NULL; if they differ it returns the first value. It is most often used to prevent division-by-zero errors.

How does NULLIF work?

NULLIF(value1, value2) checks whether value1 equals value2. If they match it returns NULL; otherwise it returns value1 unchanged.

Why do we use NULLIF?

To turn a specific value, such as a zero divisor or a placeholder like 'Unknown', into NULL, so that division, aggregate functions or COALESCE can handle it correctly.

What is the difference between NULLIF and COALESCE?

NULLIF turns a matching value into NULL. COALESCE does the opposite: it returns the first value in its list that is not NULL. They are often used together.

What is the difference between NULLIF and CASE WHEN?

NULLIF(a, b) means exactly CASE WHEN a = b THEN NULL ELSE a END. It is shorter for that one pattern; any other condition needs CASE WHEN.

How does NULLIF prevent divide-by-zero errors?

NULLIF(divisor, 0) turns a zero divisor into NULL before the division. Dividing by NULL gives NULL instead of an error.

How do data analysts use NULLIF?

To protect ratio calculations such as conversion rates, average order value and margins from division by zero, and to turn placeholder values into NULLs before averaging or counting.

Is NULLIF important in SQL interviews?

Yes. Safe division with NULLIF and combining NULLIF with COALESCE are common practical interview questions.