MySQL NOT IN: Syntax, Examples, and NULL Behavior
Learn MySQL NOT IN with value lists and subqueries, how NULL changes the result, and when NOT EXISTS is safer.
Use NOT IN to test whether a value is different from every value in a list or subquery. It is the negation of IN, but NULL can make the result NULL rather than true or false. This matters when NOT IN appears in a WHERE clause.
MySQL NOT IN syntax
expression NOT IN (value_1, value_2, ...)
The list can also come from a subquery:
expression NOT IN (SELECT column_name FROM table_name)
When neither side contains NULL, value NOT IN (a, b) is equivalent to value <> a AND value <> b. If a comparison involves NULL, SQL’s three-valued logic applies: the result can be UNKNOWN, represented by NULL in MySQL. A WHERE clause keeps only rows for which its condition is true.
For example, NOT IN returns 1 for 3 NOT IN (1, 2), 0 for 2 NOT IN (1, 2), and NULL for both NULL NOT IN (1, 2) and 3 NOT IN (1, 2, NULL).
SELECT
3 NOT IN (1, 2) AS absent,
2 NOT IN (1, 2) AS present,
NULL NOT IN (1, 2) AS null_left,
3 NOT IN (1, 2, NULL) AS null_in_list;
+--------+---------+-----------+--------------+
| absent | present | null_left | null_in_list |
+--------+---------+-----------+--------------+
| 1 | 0 | NULL | NULL |
+--------+---------+-----------+--------------+The NOT IN and NULL pitfall
Suppose a customer table contains three customers and a second table lists blocked customer IDs. The blocked-ID table allows NULL values:
CREATE TABLE customer_demo (
customer_id INT PRIMARY KEY,
customer_name VARCHAR(50) NOT NULL
);
CREATE TABLE blocked_customer_ids (
customer_id INT NULL
);
INSERT INTO customer_demo (customer_id, customer_name) VALUES
(101, 'Ava'),
(102, 'Ben'),
(103, 'Cara');
INSERT INTO blocked_customer_ids (customer_id) VALUES
(102),
(NULL);
This query looks for customers whose IDs are not in the blocked list:
SELECT customer_id, customer_name
FROM customer_demo
WHERE customer_id NOT IN (
SELECT customer_id
FROM blocked_customer_ids
);
It returns no rows. Customer 102 is an exact match, so NOT IN is false. For Ava and Cara, the comparison against the NULL in the subquery is unknown; WHERE filters out those rows too.
If the subquery can return NULL, use NOT EXISTS to express the anti-match directly:
SELECT c.customer_id, c.customer_name
FROM customer_demo AS c
WHERE NOT EXISTS (
SELECT 1
FROM blocked_customer_ids AS b
WHERE b.customer_id = c.customer_id
);
This returns Ava and Cara because no matching non-NULL blocked ID exists. See MySQL EXISTS and NOT EXISTS for more subquery examples.
+-------------+---------------+
| customer_id | customer_name |
+-------------+---------------+
| 101 | Ava |
| 103 | Cara |
+-------------+---------------+Another option is to remove NULL values from the subquery result:
SELECT customer_id, customer_name
FROM customer_demo
WHERE customer_id NOT IN (
SELECT customer_id
FROM blocked_customer_ids
WHERE customer_id IS NOT NULL
);
This also returns Ava and Cara. Use this form only when the NULL values are not meaningful to the query.
When to use NOT IN
- Use
NOT INwith a short, explicit list when the values are known to be non-NULL. - Use
NOT IN (subquery)when the subquery column is guaranteed to be non-NULL. - Use
NOT EXISTSwhen a subquery might returnNULL, or when you want to state that no matching row exists.
The IN operator behaves similarly for NULL values, but returns NULL in the corresponding cases. See MySQL IN for its syntax and examples.
For comparison semantics, see the MySQL 8.4 Comparison Functions and Operators, EXISTS and NOT EXISTS subqueries, and subquery behavior with NULL.