top of page

Welcome
to NumpyNinja Blogs

NumpyNinja: Blogs. Demystifying Tech,

One Blog at a Time.
Millions of views. 

UNDERSTANDING NULL TRAPS IN POSTGRESQL: AVOIDING COMMON PITFALLS

Introduction:

When working with SQL, handling NULL values is one of the trickiest parts of writing accurate queries. If you’re new to how NULL works in SQL, you may first want to read my earlier post on Handling NULLs in SQL:

A null trap occurs when a query unintentionally fails to account for NULLs, leading to missing data, incorrect results, or even confusing outputs. In this blog, we’ll explore what null traps are, why they happen, and how to avoid them.

WHAT IS A NULL TRAP?

“A NULL trap can be defined as unexpected or incorrect results caused by improper handling of NULL values.”

COMMON SCENARIOS WHERE WE MAY ENCOUNTER NULL TRAPS:

• Filtering

• Arithmetic

• Calculations and Aggregate Functions

• Subqueries ,Joins

• Group by, Order by

Filtering and Null Traps:

Comparisons with NULL return UNKNOWN, so rows are excluded
SELECT * FROM orders WHERE rental_date > '2026-01-01';

If rental_date is NULL, the row is excluded. To include NULLs, we need:

SELECT * FROM orders
WHERE rental_date > '2026-01-01' OR  rental_date  IS NULL;

Arithmetic and Null Traps:

Any arithmetic with Null will result in Null.
SELECT 10 + NULL;

Result : NULL

---------------------------------------------------

Example:

Table: employees

employee_id salary bonus

1 50000 5000

2 60000 NULL

3 NULL 3000

4 55000 4000

SELECT  employee_id, salary + bonus AS total_pay
FROM employees;

Result:

employee_id total_pay

1 55000

2 NULL

3 NULL

4 59000

---------------------------------------------------------------------------

Safe version:

SELECT employee_id, COALESCE(salary,0) + COALESCE(bonus,0) AS total_pay FROM employees;

Result:

employee_id total_pay

1 55000

2 60000

3 3000

4 59000

Aggregate Functions and Null Traps:

NULLs can be tricky when using aggregate functions. Usually, aggregate functions ignore nulls. “Expressions containing NULL values may result in rows being skipped unintentionally.”

Aggregate function on a single column:

SELECT SUM(salary) AS total_salary, SUM(bonus) AS total_bonus
FROM employees;

Result:

total_salary total_bonus

165000 12000

✅ NULLs are ignored, only non-NULL values are summed.

--------------------------------------------------

Aggregate function on an expression:

SELECT SUM(salary + bonus) AS total_compensation
FROM employees;

Step-by-step:

Row-wise addition (salary + bonus):

employee_id salary + bonus

1 55000

2 NULL

3 NULL

4 59000

SUM() adds only non-NULL results → 55000 + 59000 = 114000

Rows 2 and 3 are skipped because the expression evaluates to NULL—this is a classic NULL trap.

________________________________________

Safe way using COALESCE

SELECT SUM(COALESCE(salary,0) + COALESCE(bonus,0)) AS total_compensation FROM employees;

• Replace NULL with 0 before addition

• Row-wise addition now:

employee_id salary + bonus (COALESCE)

1 55000

2 60000

3 3000

4 59000

• SUM() →total_compensation = 55000 + 60000 + 3000 + 59000 = 177000 ✅

________________________________________

Key Takeaways:

1. Aggregate functions ignore NULLs by default.

2. Expressions with NULLs can produce NULL results, skipping rows in aggregates.

3. Use COALESCE() or NULLIF() to avoid null traps.

4. Always test queries with NULL values to ensure correct results.

NOT IN and Null Traps:

NOT IN is another common source of null traps in subqueries.

Consider an employees table where each employee may have a manager_id:

employee_id name manager_id

1 Alice NULL

2 Bob 1

3 Carol 1

4 Dave 2

5 Eve NULL

________________________________________

Goal

Find employees who are not managers (i.e., their employee_id is not used as a manager_id).

________________________________________

Problematic Query Using NOT IN:

SELECT name FROM employees
WHERE employee_id NOT IN (SELECT manager_id FROM employees);

What Happens?

The subquery returns:

( NULL, 1, 1, 2, NULL )

• Because manager_id contains NULL values, the list includes NULL.

• 👉 “Result: No rows returned ⚠️ (unexpected behavior)”

This is a classic null trap: the presence of NULL in the subquery causes the NOT IN condition to evaluate to UNKNOWN for all rows.”

________________________________________

Fix 1: Filter Out NULLs

SELECT name FROM employees
WHERE employee_id NOT IN (SELECT manager_id FROM employees
WHERE manager_id IS NOT NULL);

Result:

name

Carol

Dave

Eve

________________________________________

Fix 2 (Recommended): Use NOT EXISTS

SELECT e.name FROM employees e
WHERE NOT EXISTS (SELECT 1 FROM employees m
WHERE m.manager_id = e.employee_id);

Result:

name

Carol

Dave

Eve

NOT EXISTS safely ignores NULLs and is the preferred approach.

________________________________________

Key Insight

• NOT IN fails if the subquery returns even one NULL.

• Always:

o Filter out NULLs or

o Prefer NOT EXISTS

Joins and Null Traps:

Joins are used to combine data from multiple tables, but when NULL values are involved, they can lead to missing or misleading results. These are classic null traps in joins.

1. INNER JOIN and Missing Rows

Consider two tables:

employees

employee_id name department_id

1 Alice 10

2 Bob NULL

3 Carol 20

departments

department_id department_name

10 HR

20 IT

________________________________________

INNER JOIN Example

SELECT e.name, d.department_name FROM employees e
JOIN departments d
ON e.department_id = d.department_id;

Result:

name department_name

Alice HR

Carol IT

Bob is missing because department_id is NULL.

“INNER JOIN excludes rows where the join condition fails, and NULL values never match any value (NULL ≠ anything).”

________________________________________

Fix: Use LEFT JOIN

SELECT e.name, d.department_name FROM employees e
LEFT JOIN departments d
ON e.department_id = d.department_id;

Result:

name department_name

Alice HR

Bob NULL

Carol IT

LEFT JOIN preserves all rows from the left table, even when there is no match.

________________________________________

2. Filtering After LEFT JOIN (Hidden Null Trap)

A very common mistake:

SELECT e.name, d.department_name FROM employees e
LEFT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_name = 'HR';

Result:

name department_name

Alice HR

Bob disappears again!

The WHERE clause removes NULLs, effectively turning the LEFT JOIN into an INNER JOIN.

________________________________________

✅ Correct Approach

Move the filter into the JOIN condition:

SELECT e.name, d.department_name FROM employees e 
LEFT JOIN departments d
ON e.department_id = d.department_id 
AND d.department_name = 'HR';

Result:

name department_name

Alice HR

Bob NULL

Carol NULL

________________________________________

Key Takeaways

1. INNER JOIN drops rows when join columns contain NULLs.

2. LEFT JOIN preserves rows but may introduce NULLs in the result.

3. Filtering on joined columns in the WHERE clause can undo a LEFT JOIN.

4. Always be mindful that NULL = anything is not true in SQL.

5. Use LEFT JOIN and careful filtering to avoid losing data unintentionally.

Other Common Null Traps in SQL:

Apart from filtering, arithmetic, aggregates, subqueries, and joins, NULLs can also behave unexpectedly in GROUP BY and ORDER BY operations.

1. NULLs in GROUP BY

When grouping data, SQL treats NULL as a valid group value, not as “missing”.

Example:

SELECT department_id, COUNT(*) AS employee_count FROM employees
GROUP BY department_id;

Behavior:

• All rows where department_id is NULL are grouped together.

• They form a separate group, just like any other department

Result:

department_id employee_count

10 5

20 3

NULL 2

Key Insight: NULL is not ignored in GROUP BY—it becomes its own group.

2. NULLs in ORDER BY

Sorting with NULLs can be misleading because their position is database-dependent.

Example:

SELECT name, bonus FROM employees
ORDER BY bonus;

Behavior:

• In PostgreSQL, NULLs appear first by default.

• In others, they may appear last.

• This can lead to inconsistent results across systems.

________________________________________

Controlling NULL Position

In PostgreSQL, you can explicitly control where NULLs appear:

SELECT name, bonus FROM employees
ORDER BY bonus NULLS LAST; or
ORDER BY bonus NULLS FIRST;

________________________________________

Key Takeaways

1. In GROUP BY, NULLs are treated as a valid grouping value, not ignored.

2. In ORDER BY, NULLs can appear first or last depending on the database.

3. Always explicitly handle NULL behavior when consistency matters.

4. Even “simple” clauses like sorting and grouping can hide subtle null traps.

________________________________________

💡 Final Thought:

Null traps aren’t limited to complex queries—they appear in every layer of SQL. Once we understand how NULL behaves in aggregates, subqueries, joins, grouping, and sorting, we can predict and avoid most unexpected query results.

Conclusion: Writing Safe SQL with NULL Awareness

NULLs are not errors—they are part of how SQL represents missing or unknown data. However, if not handled carefully, they can lead to incorrect results, missing rows, or misleading calculations.

Across different SQL operations, we saw how NULLs behave:

• In filters, they can exclude rows unexpectedly

• In arithmetic, they can propagate and turn results into NULL

• In aggregates, they are ignored unless explicitly handled

• In subqueries, they can break comparisons like NOT IN

• In joins, they can silently drop or misalign data

• In GROUP BY and ORDER BY, they can change grouping and sorting behavior

The key takeaway is simple:

SQL does not treat NULL as a value—it treats it as unknown.

To avoid null traps, always:

• Use COALESCE() to handle missing values explicitly

• Prefer NOT EXISTS over NOT IN when NULLs may exist

• Be careful with join conditions and filtering logic

• Test queries with real-world data, including NULL cases

Understanding NULL behavior is not just a technical detail—it’s essential for writing accurate, reliable, and production-safe SQL queries.

+1 (302) 200-8320

NumPy_Ninja_Logo (1).png

Numpy Ninja Inc. 8 The Grn Ste A Dover, DE 19901

© Copyright 2025 by Numpy Ninja Inc.

  • Twitter
  • LinkedIn
bottom of page