Working with NULL
Progress Tracking
Log in to save this lesson and continue from where you left off.
The Billion Dollar Mistake
NULL is one of the most misunderstood concepts in SQL. It causes bugs, returns unexpected results, and trips up even experienced developers. But once you understand how it works, you can avoid the pitfalls.
What NULL Actually Means
NULL means "unknown" or "missing." It's not zero. It's not an empty string. It's the absence of a value.
Think of it this way:
0— means "the value is zero"''(empty string) — means "the value is blank text"NULL— means "we don't know what the value is"
This distinction matters because NULL behaves differently from actual values.
A Real Example
Let's look at our fintech_app_users table:
| user_id | user_name | phone_number |
|---|---|---|
| 1 | Emma Johnson | 8994954670 |
| 2 | Noah Williams | 7848177904 |
| 3 | Olivia Garcia | 921-171-1750 |
| 4 | Liam Martinez | |
| 5 | Ava Rodriguez |
Some users haven't provided their phone number — those values show up as NULL. We don't know their phone numbers. Maybe they never entered one. Maybe the data was lost. The point is: we don't have a value.
Finding NULL Values: IS NULL
Here's the first surprise: you can't use = to check for NULL.
-- This does NOT work:
SELECT *
FROM fintech_app_users
WHERE phone_number = NULL;This query returns nothing, even though we have rows with NULL phone numbers. Why? Because in SQL, NULL = NULL evaluates to... NULL (which is treated as false). Instead, use IS NULL.
Users Missing Phone Numbers
| user_id | user_name | phone_number |
|---|---|---|
| 1 | Emma Johnson | 8994954670 |
| 2 | Noah Williams | 7848177904 |
| 3 | Olivia Garcia | 921-171-1750 |
| 4 | Liam Martinez | |
| 5 | Ava Rodriguez |
The product team is launching a new WhatsApp notification feature and needs to identify users who haven't provided their phone numbers yet. These users will be shown a prompt to add their contact information. Find all users who have not provided a phone number. Return the `user ID` and `name`.
Excluding NULL Values: IS NOT NULL
To find rows where a value exists, use IS NOT NULL.
Find all users who have a phone number on record.
This gives you only rows where phone_number has an actual value.
Wine Varieties Tasted by Roger Voss
| id | country | description | designation | points | price | province | region_1 | region_2 | taster_name | taster_twitter_handle | title | variety | winery |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 118040 | US | A bit too soft and thus lacks structure. Shows a good array of wild berry and briary, brambly flavors. Dry and spicy, and ready now. | The Convict Rocky Ridge Vineyard | 86 | 38 | California | Rockpile | Sonoma | Paradise Ridge 2006 The Convict Rocky Ridge Vineyard Zinfandel (Rockpile) | Zinfandel | Paradise Ridge | ||
| 59743 | Italy | Aromas of toasted oak, vanilla and a confectionary note lead the nose while the palate offers mature Golden Delicious apple, butterscotch and walnut skin alongside bracing acidity. | Mongris Riserva | 88 | 30 | Northeastern Italy | Collio | Kerin O'Keefe | @kerinokeefe | Marco Felluga 2012 Mongris Riserva Pinot Grigio (Collio) | Pinot Grigio | Marco Felluga | |
| 117951 | US | Heavy in alcohol and overripe, but entirely dry, and the black currant, chocolate and pepper flavors will play well against richly sauced barbecue. | The Caboose | 84 | 30 | California | Alexander Valley | Sonoma | Starry Night 2007 The Caboose Zinfandel (Alexander Valley) | Zinfandel | Starry Night | ||
| 10202 | France | Fragrant; dusty plum and rose aromas are followed on the palate by very fruity flavors of cherries and apple skins. The fine bead and rich mousse result in a creamy mouthfeel. It's all a bit obvious and simple, but undeniably good. | Brut Rosa | 87 | 83 | Champagne | Champagne | Joe Czerwinski | @JoeCz | Ruinart NV Brut Rosa (Champagne) | Champagne Blend | Ruinart | |
| 69268 | Germany | Layers of plush yellow peach and apricot flavors highlight this easy, crowd-pleasing kabinett. Sunny lemon acidity lends refreshment to the midpalate, cutting through all the ripeness and leading to a brisk finish. Drink now through 2019. | Kabinett | 88 | 15 | Rheinhessen | Anna Lee C. Iijima | Weinreich 2012 Kabinett Riesling (Rheinhessen) | Riesling | Weinreich |
Find wine varieties tasted by 'Roger Voss' and with a value in the 'region_1' column of the dataset. Output unique variety names only.
NULL in Comparisons
NULL makes comparisons tricky. Any comparison with NULL returns NULL (treated as false):
NULL = NULL -- Returns NULL (not true!)
NULL <> NULL -- Returns NULL (not true!)
NULL > 5 -- Returns NULL
NULL = 'text'-- Returns NULLThis means rows with NULL values are excluded from most WHERE clauses, even when you might not expect it.
NULL Gets Excluded Silently
-- This misses NULL rows!
SELECT * FROM fintech_app_users
WHERE status != 'active';
-- This catches them:
SELECT * FROM fintech_app_users
WHERE phone_number IS NULL;| id | first_name | last_name | department | salary | phone_number | joining_date |
|---|---|---|---|---|---|---|
| 1 | Sarah | Mitchell | HR | 95000 | 555-0101 | 2021-03-15 |
| 2 | Michael | Chen | HR | 88000 | 555-0102 | 2022-06-01 |
| 3 | Emily | Rodriguez | HR | 82500 | 2021-09-20 | |
| 4 | David | Park | HR | 80000 | 555-0104 | 2023-01-10 |
| 5 | Lisa | Thompson | HR | 65000 | 2021-04-05 |
Find employees with no phone number from `techcorp_workforce` using IS NULL.
Users with NULL phone numbers are also excluded — because NULL <> '555-1234' evaluates to NULL, not true.
To include NULLs, explicitly check for them. Fix the query by adding OR phone_number IS NULL:
Find all users whose phone number is not '555-1234', including users who have no phone number at all.
Whenever you're filtering and NULLs might be involved, ask yourself: "Should NULL rows be included or excluded?" Then write your WHERE clause accordingly.
NULL with AND and OR
NULL follows three-valued logic (true, false, NULL). This affects how AND and OR behave:
The practical takeaway: if any part of your condition might be NULL, the whole condition might not behave as expected.
Handling NULL with COALESCE
COALESCE returns the first non-NULL value from a list. It's useful for replacing NULLs with default values:
Use COALESCE to display the text No phone for employees with a NULL phone_number.
The original data doesn't change — COALESCE just controls what appears in your results.
COALESCE can take multiple arguments. It returns the first one that isn't NULL:
COALESCE(phone_number, mobile, email, 'No contact')This checks phone number first, then mobile, then email, and falls back to 'No contact' if all are NULL.
Common Mistakes
Using = NULL Instead of IS NULL
-- Wrong:
WHERE department = NULL
-- Correct:
WHERE department IS NULLForgetting that NOT IN Excludes NULLs
-- If the list contains any NULLs, NOT IN returns nothing:
WHERE department NOT IN ('HR', NULL, 'Admin')
-- Correct: remove the NULL from the list, check separately if needed
WHERE department NOT IN ('HR', 'Admin')Assuming Empty String Equals NULL
-- These are different:
WHERE name = '' -- Empty string
WHERE name IS NULL -- Unknown valueKey Takeaways
NULLmeans unknown or missing — not zero, not empty string.- Use
IS NULLandIS NOT NULL, never= NULL. - Comparisons with
NULLreturnNULL(treated as false). - Be explicit about whether
NULLrows should be included in your results. - Use
COALESCEto replaceNULLs with default values.
What's Next
You now know how to filter data in all the ways that matter. In the next lesson, you'll learn to control the order of your results with ORDER BY — essential for any "top N" question.