IN Operator
IN checks whether a value matches any value in a given list. It is shorthand for a chain of OR-connected equality checks, and it reads much more cleanly once you have more than two or three values to compare against.Basic syntax
IN as shorthand for multiple ORs
SQL
SELECT first_name, last_name, department
FROM employees
WHERE department IN ('Engineering', 'Data Science', 'Product');This is exactly equivalent to writing:
What IN expands to
SQL
SELECT first_name, last_name, department FROM employees WHERE department = 'Engineering' OR department = 'Data Science' OR department = 'Product';
NOT IN
Excluding a list of values
SQL
SELECT first_name, last_name, department
FROM employees
WHERE department NOT IN ('Sales', 'Marketing');NOT IN with NULL is a serious, common trap
If the list passed to
NOT IN contains even a single NULL — often because the list came from a subquery rather than literal values — the entire condition can unexpectedly match *zero* rows, even ones that clearly should not be excluded. This is a consequence of SQL’s three-valued logic, where any comparison against NULL produces “unknown” rather than true or false.This silently returns no rows if excluded_ids contains a NULL
SQL
SELECT id, name FROM products WHERE id NOT IN (SELECT product_id FROM discontinued_products); -- If even one product_id in discontinued_products is NULL, -- this query returns ZERO rows, no matter what.
The safer, equivalent alternative is
NOT EXISTS, which does not suffer from this problem because it checks row existence rather than comparing values directly against a list that might contain NULL:A safer alternative to NOT IN
SQL
SELECT id, name FROM products p WHERE NOT EXISTS ( SELECT 1 FROM discontinued_products d WHERE d.product_id = p.id );
IN with a subquery
Instead of a literal list of values,
IN can also be given a subquery that produces a list of values at run time — a very common pattern for filtering one table based on results from another. We cover subqueries in depth in a dedicated section later in this series.IN with a subquery
SQL
SELECT name FROM customers WHERE id IN (SELECT customer_id FROM orders WHERE total > 500);
Tip
As a rule of thumb:
IN is fine and safe with a list of literal values you control. Once the list comes from a subquery that might contain NULL, prefer NOT EXISTS over NOT IN.