Practice8problems on this pattern, easy to hardStart practising →
Pattern 2SQL course / Foundations

SQL NULL: Working with missing and unknown values correctly

Learn why NULL values can leave rows out of a query, how to check for missing values, and when to use a replacement.

17 min read13 sections4 predictions8 practice problems

In P01 we used WHERE to find rows that met a requirement. For example, WHERE status = 'paid' kept orders whose status was 'paid'. That comparison is easy to understand when every row has a well-defined status. But what should happen when the value we want to compare is missing?

SQL represents a missing value with NULL. In this lesson, we will use a small orders table to see how NULL affects comparisons, filters, and calculations. These examples will also help us understand why a NOT IN query can return no rows even when some rows appear to meet its requirement.

01

Understanding NULL

The following orders table contains six orders. Some have a discount code and a shipping time, while others have NULL in those columns. We will use this table for the order examples below:

orders6 rows
order_idcustomerproductquantityunit_pricediscount_codediscount_amountshipped_atstatus
1001AshaWireless Mouse2750SAVE101502026-07-02 10:30paid
1002BenMechanical Keyboard13200NULLNULLNULLpending
1003CarlaLaptop Sleeve31200WELCOME3002026-07-05 09:15paid
1004DevOffice Chair18500NULLNULLNULLcancelled
1005AshaWireless Mouse1750''NULL2026-07-07 17:20paid
1006BenMonitor Stand21800SAVE10200NULLrefunded

Order 1002 is pending, and its shipped_at is NULL. This may mean the order has not shipped yet. Order 1004 is cancelled, and its shipped_at is also NULL. This order may never need a shipping time. Both orders have NULL, but possibly for different reasons. NULL only tells us a value is missing or unknown, not the reason behind.

Order 1005 has '' in discount_code. This is an empty string, meaning text with no characters. In these examples, NULL, '', and 'NULL' are different:

  • NULL means no value is given.
  • '' is an empty text value.
  • 'NULL' is a text value containing the four letters NULL.
02

NULL and UNKNOWN

Suppose we want to find the orders that used the discount code 'SAVE10'. We can write the same kind of equality comparison that we used in P01:

Query
SELECT *
FROM orders
WHERE discount_code = 'SAVE10';
  • For order 1001, discount_code contains 'SAVE10', so the condition is TRUE.
  • For order 1003, it contains 'WELCOME', so the condition is FALSE.
  • For order 1002, however, discount_code is NULL. SQL has no way of knowing what that missing value represented by NULL is and how to compare it with 'SAVE10'. So it cannot give a definite TRUE or FALSE answer. Hence, the comparison returns UNKNOWN.
discount_code = 'SAVE10', row by row6 rows
order_idComparison for this rowResult of the comparison
1001'SAVE10' = 'SAVE10'TRUE
1002NULL = 'SAVE10'UNKNOWN
1003'WELCOME' = 'SAVE10'FALSE
1004NULL = 'SAVE10'UNKNOWN
1005'' = 'SAVE10'FALSE
1006'SAVE10' = 'SAVE10'TRUE

This comparison matters because WHERE filters a row only when its condition is TRUE, and it leaves out rows whose condition is FALSE and rows whose condition is UNKNOWN. The query therefore returns orders 1001 and 1006, the two orders with a given 'SAVE10' code.

Three-valued logic

As can be seen from the above table, a comparison operation can return three different values - TRUE, FALSE and UNKNOWN - SQL calls these three possible answers three-valued logic. This is because an ordinary comparison with NULL cannot establish an answer, so it returns UNKNOWN rather than TRUE or FALSE.

The same rule applies to <>, >, <, >=, and <=. For example, NULL > 100 is UNKNOWN because the missing number might be above 100 or below it. Even NULL = NULL and NULL <> NULL are UNKNOWN: two missing values do not tell us whether their values would be equal or different.

If you run these comparisons directly, your SQL tool will usually display NULL for the unknown result. We use the word UNKNOWN when explaining the logic:

Query
SELECT 120 > 100; -- TRUE
SELECT 80 > 100; -- FALSE
SELECT NULL > 100; -- NULL, meaning UNKNOWN
SELECT NULL = NULL; -- NULL, meaning UNKNOWN
SELECT NULL <> NULL; -- NULL, meaning UNKNOWN

Now that we know why = cannot compare a missing value, we can choose the correct way to search for one.

03

Checking for NULL

Suppose we need orders that have no given shipping time. It is tempting to use = NULL, just as we used = 'SAVE10' for a discount code:

Query
-- This query does not find the missing shipping times.
SELECT
order_id,
customer,
shipped_at
FROM orders
WHERE shipped_at = NULL;

For an order with a given shipping time, comparing that time with NULL returns UNKNOWN. For an order with no shipping time, the comparison becomes NULL = NULL, which also returns UNKNOWN. Since no row produces TRUE, this query returns no rows.

To correctly check whether a value is missing in SQL, use IS NULL. Unlike = NULL, this condition tests whether the column contains NULL and gives a definite TRUE or FALSE answer:

Query
SELECT
order_id,
customer,
shipped_at,
status
FROM orders
WHERE shipped_at IS NULL;
Result3 rows
order_idcustomershipped_atstatus
1002BenNULLpending
1004DevNULLcancelled
1006BenNULLrefunded
  • For order 1002, shipped_at IS NULL is TRUE, so the row is included.
  • For order 1001, a shipping time is given, so shipped_at IS NULL is FALSE and the row is excluded.

Notice that the three returned orders have different statuses. The query finds orders without a given shipping time.

However, if we want orders with a given shipping time instead, use IS NOT NULL:

Query
SELECT
order_id,
customer,
shipped_at,
status
FROM orders
WHERE shipped_at IS NOT NULL;
Result3 rows
order_idcustomershipped_atstatus
1001Asha2026-07-02 10:30paid
1003Carla2026-07-05 09:15paid
1005Asha2026-07-07 17:20paid

These two conditions let us choose rows based on whether a value is given or not. Use IS NULL to find rows where a value is missing. Use IS NOT NULL to find rows where a value is given.

04

When Filters Drop NULL

We can now return to the discount codes example with a more specific requirement:

RequirementReturn every order that did not use 'SAVE10', including orders where the discount code was NULL.

The requirement explicitly includes orders whose discount_code is NULL. We therefore expect orders 1002, 1003, 1004, and 1005. A first attempt might use <> to exclude 'SAVE10', but let us check what that query actually does.

Pause and predictPredict before you continue

Which orders does this query return?

The query
SELECT
order_id,
customer,
discount_code
FROM orders
WHERE discount_code <> 'SAVE10';

Choose the corrected condition

The query needs to allow a value different from 'SAVE10' or a missing value.

Pause and predictPredict before you continue

Which of these conditions expresses both parts of the requirement?

The query
-- Option A
WHERE discount_code <> 'SAVE10'
-- Option B
WHERE discount_code = NULL
OR discount_code <> 'SAVE10'
-- Option C
WHERE discount_code <> 'SAVE10'
OR discount_code IS NULL
-- Option D
WHERE NOT discount_code = 'SAVE10'
05

Comparisons with NULL

How AND, OR, and NOT handle UNKNOWN

The corrected filter works because OR can give a definite answer even when one part is UNKNOWN. AND works differently: every part must be TRUE. For example, TRUE AND UNKNOWN is UNKNOWN, while FALSE AND UNKNOWN is FALSE because one false condition is already enough to fail an AND condition.

Rule of thumb

Rank the answers: TRUE on top, UNKNOWN in the middle, FALSE at the bottom. AND keeps the lower of the two; OR keeps the higher.

NOT turns the ranking upside down. UNKNOWN is the middle rung, so it stays UNKNOWN.

First condition
Operator
Second condition
TRUEhighest
UNKNOWNmiddle
FALSElowest
First
Second
Resultlower wins
UNKNOWN
Why
TRUEANDUNKNOWN→UNKNOWN

AND keeps the lower answer. One part is TRUE, but the other is not known, so SQL cannot say that both are true.

In a WHERE clause, this row would be left out: WHERE keeps a row only when its condition is TRUE.

Every combination

Rows are the first condition, columns the second, both in rank order. Select a square to see it on the ladder. Swapping the two conditions gives the same answer, so each map mirrors across its diagonal.

ANDthe lower answer wins
1st ↓ 2nd →TRUEUNKNOWNFALSETRUEUNKNOWNFALSE
ORthe higher answer wins
1st ↓ 2nd →TRUEUNKNOWNFALSETRUEUNKNOWNFALSE
NOTthe ranking flips
conditionafter NOTTRUE→UNKNOWN→FALSE→

Use the same reasoning for numbers

The question about missing values also applies to discount_amount. If we want confirmed discounts above 100, WHERE discount_amount > 100 is enough. It returns orders 1001, 1003, and 1006; missing discount amounts do not pass that comparison.

Suppose instead that the requirement asks for discounts of 100 or less, including orders with no discount. Then we write both conditions:

Query
WHERE discount_amount <= 100
OR discount_amount IS NULL

This returns orders 1002, 1004, and 1005. Writing only WHERE discount_amount <= 100 would return no rows from this table. The choice depends on the requirement.

06

Fallbacks with COALESCE

So far, we have used conditions in WHERE to decide which rows to return. Sometimes the requirement might be different: keep the rows, but display a useful value where a column is NULL. For example, we may want to show 'No discount' instead of a missing discount code.

To implement this, we can use COALESCE in the SELECT part of the query:

Query
SELECT
order_id,
customer,
discount_code,
COALESCE(discount_code, 'No discount') AS displayed_discount
FROM orders;

COALESCE(discount_code, 'No discount') returns discount_code when it is not NULL. When it is NULL, it returns 'No discount' instead. Also, using AS and then some text, as in AS displayed_discount, gives the resulting column a name.

Result6 rows
order_idcustomerdiscount_codedisplayed_discount
1001AshaSAVE10SAVE10
1002BenNULLNo discount
1003CarlaWELCOMEWELCOME
1004DevNULLNo discount
1005Asha''''
1006BenSAVE10SAVE10
  • For order 1001, the first value is 'SAVE10', so COALESCE returns it.
  • For order 1002, the first value is NULL, so COALESCE returns the second value, 'No discount'.
  • Order 1005 still shows '': an empty string is not NULL in these examples, so it is returned as it is.

The values passed to COALESCE must have types that the database can use together. In our display example, both the discount code and its replacement are text. For a numeric calculation, use a numeric replacement such as 0.

Using COALESCE in a calculation

Suppose we want to calculate an order amount by multiplying the quantity by the unit price, then subtracting the discount:

Query
quantity * unit_price - discount_amount
  • For order 1001, the calculation is 2 * 750 - 150, which gives 1350.
  • For order 1002, it is 1 * 3200 - NULL, which gives NULL. We know the quantity and price, but the missing discount means the expression cannot establish the final amount.

If we know that a missing discount_amount means no discount was applied, we can use COALESCE(discount_amount, 0) to subtract zero in those rows:

Query
SELECT
order_id,
quantity * unit_price AS gross_amount,
discount_amount,
quantity * unit_price
- COALESCE(discount_amount, 0) AS final_amount
FROM orders;
Result6 rows
order_idgross_amountdiscount_amountfinal_amount
100115001501350
10023200NULL3200
100336003003300
10048500NULL8500
1005750NULL750
100636002003400

Order 1002 now has a calculated amount of 3200 because its missing discount is treated as zero. The other missing discounts are handled in the same way.

07

Converting Values with NULLIF

Our display query still leaves order 1005 with an empty discount code. We cannot fix that by adding another replacement to COALESCE, because COALESCE already finds a given value: ''. If the requirement is that an empty value should mean the same thing as a missing value, then we first need to turn '' into NULL. NULLIF(value1, value2) does that. It returns NULL if value1 = value2; otherwise, it returns value1.

For our discount codes, the expression is NULLIF(discount_code, ''):

Query
SELECT
order_id,
discount_code,
NULLIF(discount_code, '') AS normalized_discount_code
FROM orders;
Result6 rows
order_iddiscount_codenormalized_discount_code
1001SAVE10SAVE10
1002NULLNULL
1003WELCOMEWELCOME
1004NULLNULL
1005''NULL
1006SAVE10SAVE10
  • For order 1005, NULLIF('', '') returns NULL because the two empty strings compare equal.
  • For order 1001, NULLIF('SAVE10', '') returns 'SAVE10' because the values are different.

An existing NULL also remains NULL in the result. Now both forms of missing code produce NULL, so we can use the same COALESCE replacement for them:

Query
SELECT
order_id,
customer,
COALESCE(
NULLIF(discount_code, ''),
'No discount'
) AS discount
FROM orders;
Result6 rows
order_idcustomerdiscount
1001AshaSAVE10
1002BenNo discount
1003CarlaWELCOME
1004DevNo discount
1005AshaNo discount
1006BenSAVE10
08

IN, NOT IN, and NULL

We have seen how NULL affects = and <>. The same comparisons are involved when we check a list of values with IN or NOT IN. To see this, return to the orders table and find orders whose discount code is either 'SAVE10' or 'WELCOME':

Query
SELECT
order_id,
discount_code
FROM orders
WHERE discount_code IN ('SAVE10', 'WELCOME');

Orders 1001, 1003, and 1006 are returned because their given codes match one of the values in the list. Orders with a NULL code are excluded because SQL cannot establish a match. The empty code in order 1005 is also excluded, but for a different reason: '' is a value that matches neither code.

For this two-value list, discount_code IN ('SAVE10', 'WELCOME') has the same comparison meaning as:

Query
WHERE discount_code = 'SAVE10'
OR discount_code = 'WELCOME'

If discount_code is NULL, both comparisons return UNKNOWN. Combining them with OR still gives UNKNOWN, so the row is excluded.

NOT IN does not include the row with a missing code. A comparison with NULL produces UNKNOWN. Applying NOT to UNKNOWN still gives UNKNOWN, so the row is excluded.

ValueIN ('SAVE10', 'WELCOME')NOT IN ('SAVE10', 'WELCOME')
SAVE10TRUEFALSE
WELCOMETRUEFALSE
OTHERFALSETRUE
NULLUNKNOWNUNKNOWN

Use this Orders table for Pause and Predict question 3 given below:

orders4 rows
order_idstatus
1paid
2pending
3cancelled
4NULL
Pause and predictPredict before you continue

Which orders does this query return?

The query
SELECT order_id, status
FROM orders
WHERE status NOT IN ('cancelled', 'refunded');
09

Spotting NULL Issues

In the previous section, a missing value in a row caused that row to be excluded. There is a second situation to watch for: a NULL value inside the list used by NOT IN. That can prevent every row from passing the condition, including rows with non-null values.

Consider this condition on the original orders table:

Query
WHERE discount_code NOT IN ('SAVE10', 'WELCOME', NULL)

To understand the problem, imagine an order with the code 'SPRING20'. For 'SPRING20' to be outside the list, it must differ from every listed value. SQL must therefore establish all three comparisons:

ComparisonResult
'SPRING20' <> 'SAVE10'TRUE
'SPRING20' <> 'WELCOME'TRUE
'SPRING20' <> NULLUNKNOWN

The NOT IN comparisons are combined with AND. The first two are TRUE, but the last is UNKNOWN, so TRUE AND TRUE AND UNKNOWN is UNKNOWN. Hence, the row cannot pass the WHERE condition. We can check the same result directly (Remember we can write any expression in the SELECT clause):

Query
SELECT 'SPRING20' NOT IN ('SAVE10', 'WELCOME', NULL);
-- NULL, meaning UNKNOWN

A discount code already in the list behaves differently. For example, 'SAVE10' NOT IN ('SAVE10', 'WELCOME', NULL) is FALSE because there is a definite match. Both FALSE and UNKNOWN are excluded by WHERE, so this list containing NULL returns no row.

Using the same problem with a subquery

A list is not always written directly into the query. It can come from another SELECT, called a subquery. Suppose we have these two tables and want customers who have no matching ID in blocked_customers.

customers3 rows
customer_idcustomer
1Asha
2Ben
3Carla
blocked_customers2 rows
customer_id
2
NULL

We expect Asha and Carla because only Ben’s ID, 2, is listed as a known blocked ID. The following query appears to express that requirement.

Pause and predictPredict before you continue

What does this query actually return?

The query
SELECT customer_id, customer
FROM customers
WHERE customer_id NOT IN (
SELECT customer_id
FROM blocked_customers
);

Use NOT EXISTS to find whether a matching row exists

Our requirement is to return a customer if no row in blocked_customers has the same ID as that in the Customers table. NOT EXISTS can be used to do it in a cleaner way:

Query
SELECT c.customer_id, c.customer
FROM customers AS c
WHERE NOT EXISTS (
SELECT 1
FROM blocked_customers AS b
WHERE b.customer_id = c.customer_id
);

Inside EXISTS, SELECT 1 is a common way to write the query because we only need to know whether any matching row is found; we do not need its selected value.

For Asha, the inner WHERE condition looks for a blocked ID equal to 1. Neither 2 = 1 nor NULL = 1 gives TRUE, so the inner query finds no matching row. NOT EXISTS is therefore TRUE, and Asha is included. For Ben, 2 = 2 is TRUE, so a matching row exists and Ben is excluded. Carla has no matching blocked ID and is included.

Result2 rows
customer_idcustomer
1Asha
3Carla

The NULL in blocked_customers does not match any known customer ID, so it does not remove unrelated customers from this result.

Another option: remove NULL before using NOT IN

For the same three customers, we can also filter missing IDs out of the subquery:

Query
SELECT customer_id, customer
FROM customers
WHERE customer_id NOT IN (
SELECT customer_id
FROM blocked_customers
WHERE customer_id IS NOT NULL
);

The subquery now returns only 2, so the outer query returns Asha and Carla. Both corrected queries work for these tables because every ID in customers is given.

What if a customer also has a missing ID?

Until now, every row in customers had a given customer_id. Let us add Dev, whose ID is missing, and keep blocked_customers unchanged.

customers4 rows
customer_idcustomer
1Asha
2Ben
3Carla
NULLDev
blocked_customers2 rows
customer_id
2
NULL

We still want customers with no matching blocked ID. But for Dev, we also need to decide what a missing customer ID should mean.

NOT EXISTS includes Dev when the comparison uses =

Consider the same NOT EXISTS query:

Query
SELECT c.customer_id, c.customer
FROM customers AS c
WHERE NOT EXISTS (
SELECT 1
FROM blocked_customers AS b
WHERE b.customer_id = c.customer_id
);

For Dev, c.customer_id is NULL. The inner query checks whether either blocked ID matches that missing ID:

Blocked IDComparison for DevResult
22 = NULLUNKNOWN
NULLNULL = NULLUNKNOWN

Neither comparison produces TRUE, so neither blocked row passes the inner WHERE condition. The inner query therefore returns no matching rows.

EXISTS is FALSE when its query returns no rows, so NOT EXISTS is TRUE. Dev is included:

Result3 rows
customer_idcustomer
1Asha
3Carla
NULLDev

NOT IN excludes Dev even after we remove missing blocked IDs

Now use the corrected NOT IN query from the previous example, which removes NULL from the blocked-ID list:

Query
SELECT customer_id, customer
FROM customers
WHERE customer_id NOT IN (
SELECT customer_id
FROM blocked_customers
WHERE customer_id IS NOT NULL
);

The subquery returns only 2. For Asha and Carla, the comparison is straightforward: their IDs are different from 2, so they are included. Ben’s ID matches 2, so he is excluded.

For Dev, however, the condition becomes: NULL NOT IN (2)

That condition is UNKNOWN, because SQL cannot establish whether the missing ID differs from 2. The outer WHERE therefore excludes Dev:

Result2 rows
customer_idcustomer
1Asha
3Carla

Removing NULL from blocked_customers fixes the problem inside the list. It does not change how NOT IN handles a missing value in customers.customer_id.

Explicitly exclude customers whose IDs are missing

Suppose the requirement says: “Return customers with a given ID who have no matching blocked ID.”

We can express both parts using IS NOT NULL and NOT EXISTS:

Query
SELECT c.customer_id, c.customer
FROM customers AS c
WHERE c.customer_id IS NOT NULL
AND NOT EXISTS (
SELECT 1
FROM blocked_customers AS b
WHERE b.customer_id = c.customer_id
);

Dev now fails c.customer_id IS NOT NULL, so he is excluded even though the NOT EXISTS condition is TRUE. Asha and Carla satisfy both conditions and are returned:

Result2 rows
customer_idcustomer
1Asha
3Carla

The query now states the requirement directly: the customer must have a given ID, and no blocked row may match that ID.

Tip
NOT IN against an empty subquery is true, even for a NULL outer value.
10

What Missing Values Mean

Most examples so far have treated NULL as an unknown value. Sometimes, however, a requirement asks whether two fields have the same given value, treating two missing fields as the same. For example, when comparing an old discount code with a new discount code, we may want two missing codes to count as unchanged.

As we saw earlier, NULL = NULL returns UNKNOWN. In DuckDB and PostgreSQL, IS NOT DISTINCT FROM gives the definite equality result needed for this requirement:

Query
SELECT NULL = NULL; -- NULL, meaning UNKNOWN
SELECT NULL IS NOT DISTINCT FROM NULL; -- TRUE
SELECT NULL IS NOT DISTINCT FROM 'SAVE10'; -- FALSE

With two given values, IS NOT DISTINCT FROM behaves like =. With two NULL values, it returns TRUE. With one given value and one NULL, it returns FALSE. This is called NULL-safe equality because the comparison gives a definite answer even when a value is missing.

The corresponding difference test is IS DISTINCT FROM. It returns TRUE when the given values differ, or when one is missing and the other is given:

First valueSecond valueIS NOT DISTINCT FROMIS DISTINCT FROM
SAVE10SAVE10TRUEFALSE
SAVE10WELCOMEFALSETRUE
NULLNULLTRUEFALSE
NULLSAVE10FALSETRUE

Use old_code IS NOT DISTINCT FROM new_code to check whether two values are equal, treating two NULL values as equal. Use old_code IS DISTINCT FROM new_code to check whether they differ, treating one NULL and one non-NULL value as different.

Use this rule only when it fits the question. Two missing discount-code fields may reasonably mean “unchanged,” but two missing customer IDs do not prove that they belong to the same customer.

11

Choose the Correct Query

The examples all come back to the same decision: what should happen to a row when one of its values is missing? If you need to find the missing value itself, use IS NULL. If you are comparing it with another value, remember that an ordinary comparison returns UNKNOWN, which does not pass a WHERE condition. When the requirement says to include missing values, write that choice explicitly with a condition such as OR discount_code IS NULL.

COALESCE can replace a missing value with a fallback. NULLIF can turn a specific value, such as '', into NULL.

What you want to doSQL from the examples
Find missing shipping timesWHERE shipped_at IS NULL
Find given shipping timesWHERE shipped_at IS NOT NULL
Include codes other than SAVE10 and missing codesWHERE discount_code <> 'SAVE10' OR discount_code IS NULL
Display a label for missing codesCOALESCE(discount_code, 'No discount')
Use zero for a confirmed missing discountCOALESCE(discount_amount, 0)
Treat an empty code as missingNULLIF(discount_code, '')
Display the same label for an empty or missing codeCOALESCE(NULLIF(discount_code, ''), 'No discount')
Find customers with no matching blocked rowWHERE NOT EXISTS (SELECT 1 FROM blocked_customers AS b WHERE b.customer_id = c.customer_id) using the c alias from section 09
Compare two values, treating two NULLs as equalold_code IS NOT DISTINCT FROM new_code

If a query leaves out more rows than expected, work through one row with a missing value. Check the result of each comparison, then combine those results using AND, OR, or NOT. This lets you explain why the row was excluded and whether the filter condition needs to change.

12

Five Key Takeaways

  1. NULL means a value is missing or unknown. It does not tell us why the value is missing.
  2. A comparison with NULL, even NULL = NULL, returns UNKNOWN, and WHERE keeps only rows whose condition is TRUE.
  3. Use IS NULL and IS NOT NULL to check for missing values. When the requirement includes missing values, add OR column_name IS NULL.
  4. COALESCE replaces a missing value with a fallback, and NULLIF turns a specific value, such as '', into NULL.
  5. One NULL in a NOT IN list makes the condition UNKNOWN for every row that is not in the list. Use NOT EXISTS, or remove NULL from the list first.
13

Summary

In this pattern, we learned how NULL affects comparisons, filters, and calculations. We used IS NULL and IS NOT NULL to find missing values, COALESCE and NULLIF to handle them in results, and NOT EXISTS to avoid the NOT IN trap.

In P03, we will use ORDER BY, ASC, DESC, and LIMIT to control the order and number of rows returned. You now have the rules needed to decide which rows should reach that result in the first place.

Practise this pattern

Now write it yourselfBrowse NULL practice →

Graded problems on this pattern, easy to hard. They run in your browser against real tables – no sign-up needed.

Up next · Pattern 3Ordering and top-N

You can now decide which rows belong in the result. Next, you will control their order and how many are returned.

Go deeper

Ask Colearn about this pattern

Not sure why your own query is dropping rows or turning blank? Ask about the concept, or paste a simplified version of your query.

Instant answers, grounded in the same verified material the diagnostic grades against.

3 of 3 free questions left
Why does NOT discount_code = 'SAVE10' still exclude NULL codes?

For a missing code, discount_code = 'SAVE10' is unknown, and NOT unknown is still unknown. A filter keeps only rows where the condition is true, so the row is dropped.

If missing codes should qualify, say so explicitly: discount_code <> 'SAVE10' OR discount_code IS NULL.

Why does this COALESCE leave an empty string unchanged?

COALESCE replaces only NULL. An empty string is a known value, so it is returned as-is.

If an empty string should mean missing too, normalise it first: COALESCE(NULLIF(discount_code, ''), 'No discount').

How do a NULL outer ID and a NULL inner ID affect this exclusion query?

A NULL inside a NOT IN list makes every non-matching comparison unknown, so NOT IN can return no rows at all. NOT EXISTS is not affected: the NULL inner row simply never matches.

A NULL outer ID is a separate decision: NOT EXISTS keeps it, because = cannot match it, while NOT IN against a nonempty list drops it. Add an IS NOT NULL check on the outer ID if missing IDs should not qualify.

When should I use IS NOT DISTINCT FROM?

When two missing values should count as equal, such as checking whether an optional field changed. Ordinary = gives unknown if either side is NULL.

Use it only when the requirement calls for it: two missing customer IDs do not necessarily identify the same customer. Syntax and support vary by database.

Don’t paste credentials, personal data, or confidential production records.

For questions about SUM, distinguish missing arithmetic inputs from aggregate behaviour: SUM skips NULL inputs and returns NULL when there are no non-NULL inputs. Defaulting a calculation’s inputs and defaulting an aggregate’s final result express different policies. P04 teaches aggregation.