NULL Handling
title: “NULL Handling (IS NULL / IS NOT NULL)” part: 1 topic_number: 19 slug: “null-handling” difficulty: “Beginner” prerequisites: “select-from-where” —
NULL Handling
What Is It?
NULL represents “unknown” or “missing” data — not zero, not empty string, but the absence of a value. NULL behaves differently from regular values, and you need special operators (IS NULL / IS NOT NULL) to work with it.
Real-world analogy: A form field left blank. It’s not that the answer is “0” or “” — it’s that no answer was provided.
Understanding NULL
NULL is NOT Zero
1
2
SELECT * FROM employees WHERE salary = 0; -- Finds employees with $0 salary
SELECT * FROM employees WHERE salary IS NULL; -- Finds employees with NO salary info
These are different!
salary = 0→ The salary exists and is zero dollarssalary IS NULL→ No salary information exists
NULL is NOT Empty String
1
2
SELECT * FROM employees WHERE first_name = ''; -- Finds employees with empty string name
SELECT * FROM employees WHERE first_name IS NULL; -- Finds employees with NO name
Again, different!
first_name = ''→ Name exists but is emptyfirst_name IS NULL→ No name was provided
Basic Examples
Find Employees with No Salary
1
2
3
SELECT first_name, last_name, salary
FROM employees
WHERE salary IS NULL;
Expected output:
| first_name | last_name | salary |
|---|---|---|
| Noah | Harris | NULL |
| Paul | Garcia | NULL |
Cannot use = NULL! Always use IS NULL.
Find Employees with a Salary
1
2
3
SELECT first_name, last_name, salary
FROM employees
WHERE salary IS NOT NULL;
Expected output: All 18 employees who have salaries assigned.
Going Deeper
NULL in Comparisons (Always False/Unknown)
1
2
3
4
5
6
-- These all return FALSE (or NULL, technically)
NULL = NULL -- Not TRUE!
NULL != NULL
NULL > 100
NULL < 100
100 + NULL
Key rule: Any comparison with NULL results in NULL (unknown), which is treated as FALSE in WHERE clauses.
Example:
1
SELECT * FROM employees WHERE manager_id = NULL;
Returns: Empty set (0 rows)
Why: manager_id = NULL evaluates to NULL for every row, even rows where manager_id IS NULL. NULL = NULL is not TRUE!
1
2
-- • CORRECT
SELECT * FROM employees WHERE manager_id IS NULL;
Returns: Alice and Paul (employees with no manager)
NULL in Arithmetic (Poison!)
1
2
3
4
5
6
SELECT
first_name,
salary,
salary + 5000 AS salary_with_bonus
FROM employees
WHERE first_name = 'Noah';
Expected output:
| first_name | salary | salary_with_bonus |
|---|---|---|
| Noah | NULL | NULL |
NULL + 5000 = NULL! Any arithmetic operation with NULL produces NULL.
This is called the “NULL poison” effect — NULL spreads through calculations.
NULL in Aggregates
1
2
3
4
5
6
SELECT
COUNT(*) AS total_employees,
COUNT(salary) AS employees_with_salary,
SUM(salary) AS total_payroll,
AVG(salary) AS avg_salary
FROM employees;
Expected output:
| total_employees | employees_with_salary | total_payroll | avg_salary |
|---|---|---|---|
| 20 | 18 | 1633000.00 | 90722.22 |
Key points:
COUNT(*)counts all rows (including NULLs)COUNT(salary)counts only non-NULL valuesSUMandAVGignore NULL values
Pause and Predict: What does
MIN(manager_id)return if some manager_ids are NULL?
Answer
`MIN(manager_id)` returns 1 (the smallest non-NULL manager_id). NULL values are ignored by MIN, MAX, SUM, AVG — they only process non-NULL values.Watch Out — Common Mistakes
Mistake #1: Using = NULL or != NULL
1
2
3
-- WRONG — Never matches anything!
SELECT * FROM employees WHERE salary = NULL;
SELECT * FROM employees WHERE salary != NULL;
Both return empty sets! Comparisons with NULL always evaluate to NULL (treated as FALSE).
1
2
3
-- • CORRECT
SELECT * FROM employees WHERE salary IS NULL;
SELECT * FROM employees WHERE salary IS NOT NULL;
Mistake #2: Forgetting NULL in NOT IN
1
2
3
4
-- TRICKY — Doesn't work as expected if the subquery contains NULL
SELECT *
FROM employees
WHERE department_id NOT IN (1, 2, NULL);
Expected: Employees not in departments 1 or 2
What happens: Returns empty set!
Why: department_id NOT IN (1, 2, NULL) is equivalent to:
department_id != 1 AND department_id != 2 AND department_id != NULL- The last condition (
!= NULL) is always FALSE/NULL - So the entire AND condition fails
The fix:
1
2
3
4
-- • CORRECT — Filter out NULLs
SELECT *
FROM employees
WHERE department_id NOT IN (1, 2) AND department_id IS NOT NULL;
Or use NOT EXISTS:
1
2
3
4
5
6
SELECT *
FROM employees e
WHERE NOT EXISTS (
SELECT 1 FROM (SELECT 1 AS id UNION SELECT 2 UNION SELECT NULL) vals
WHERE vals.id = e.department_id
);
Mistake #3: NULL in CONCAT (String Concatenation)
1
2
3
SELECT CONCAT(first_name, ' ', last_name, ' - Dept: ', department_id)
FROM employees
WHERE first_name = 'Paul';
Expected: “Paul Garcia - Dept: NULL”
What you get: NULL (entire result is NULL)
Why: CONCAT with any NULL argument returns NULL.
The fix: Use COALESCE or IFNULL (we’ll cover COALESCE in Part 2):
1
2
3
SELECT CONCAT(first_name, ' ', last_name, ' - Dept: ', IFNULL(department_id, 'None'))
FROM employees
WHERE first_name = 'Paul';
Output: “Paul Garcia - Dept: None”
Edge Case Spotlight
ORDER BY with NULLs
1
2
3
SELECT first_name, salary
FROM employees
ORDER BY salary ASC;
MySQL sorts NULL values first in ASC order (treated as “lowest” values).
Output (top rows):
| first_name | salary |
|---|---|
| Noah | NULL |
| Paul | NULL |
| Ivy | 55000.00 |
| … | … |
In DESC order:
1
2
3
SELECT first_name, salary
FROM employees
ORDER BY salary DESC;
NULLs appear last (still treated as “lowest”).
To control NULL positioning:
1
2
3
4
-- Put NULLs last even in ASC
SELECT first_name, salary
FROM employees
ORDER BY salary IS NULL, salary ASC;
How this works: salary IS NULL evaluates to 0 (false) or 1 (true). Sorts FALSE (0) first, so non-NULLs appear first.
Try This
Exercise 1 (Guided)
Find all employees who don’t have a manager assigned. Show their first_name, last_name, and manager_id.
Hint
WHERE manager_id IS NULLExercise 2 (Independent)
Count how many employees have a manager vs. how many don’t. Show both counts in one query.
Hint
Use COUNT with two different conditions. You might need SUM with a CASE statement, or two separate COUNT expressions.Exercise 3 (Challenge)
Find employees whose salary is unknown (NULL) or less than $60,000. Show their name and salary, sorted by salary (with NULLs last).
Answer Key
Exercise 1 Answer
```sql SELECT first_name, last_name, manager_id FROM employees WHERE manager_id IS NULL; ``` **Expected output:** | first_name | last_name | manager_id | |------------|-----------|------------| | Alice | Johnson | NULL | | Paul | Garcia | NULL |Exercise 2 Answer
**Approach 1: Two separate queries (simple but not elegant):** ```sql SELECT COUNT(*) AS has_manager FROM employees WHERE manager_id IS NOT NULL; SELECT COUNT(*) AS no_manager FROM employees WHERE manager_id IS NULL; ``` **Approach 2: One query with CASE:** ```sql SELECT SUM(CASE WHEN manager_id IS NOT NULL THEN 1 ELSE 0 END) AS has_manager, SUM(CASE WHEN manager_id IS NULL THEN 1 ELSE 0 END) AS no_manager FROM employees; ``` **Expected output:** | has_manager | no_manager | |-------------|------------| | 18 | 2 | **Approach 2 is more elegant** — all in one query.Exercise 3 Answer
```sql SELECT first_name, last_name, salary FROM employees WHERE salary IS NULL OR salary < 60000 ORDER BY salary IS NULL, salary ASC; ``` **Expected output:** | first_name | last_name | salary | |------------|-----------|--------| | Ivy | Taylor | 55000.00 | | Tina | Rodriguez | 58000.00 | | Noah | Harris | NULL | | Paul | Garcia | NULL | **Explanation:** - WHERE captures both NULL salaries and salaries < $60K - `ORDER BY salary IS NULL, salary ASC` sorts non-NULLs first (ascending), then NULLs at the end **Alternative (NULLs first):** ```sql ORDER BY salary ASC; -- Default behavior, NULLs first ```Quick Recap
• NULL represents unknown/missing data — not zero, not empty string
• Use IS NULL and IS NOT NULL — never = NULL or != NULL
• NULL in comparisons always evaluates to NULL (treated as FALSE)
• NULL in arithmetic makes the entire result NULL (“NULL poison”)
• Aggregates (SUM, AVG, MIN, MAX) ignore NULL values
• COUNT(*) includes NULLs, COUNT(column) excludes them
• Be careful with NOT IN when the list contains NULL
Up Next
Next topic: String Functions (UPPER, LOWER) → part1_20_string_functions_upper_lower.md
You’ve mastered the tricky world of NULL! Next, you’ll learn useful string functions to manipulate text data!