liveBook · Manning

NOT IN

As explained in PostgreSQL Mistakes and How to Avoid Them

NOT IN is a SQL predicate that tests whether a value is absent from a set of values or subquery results, subject to SQL null semantics.

NOT IN is a negative predicate for excluding matching values from a constant list or subquery result. It is concise for small, known lists, but NULL values can make an apparently valid exclusion return no rows. section 2.1 section 2.11

The supplier-state trap

A promotion query attempts to find customers in states without suppliers: `WHERE state NOT IN (SELECT state FROM erp.suppliers)`. Although a customer exists in Kansas, the query returns zero rows because at least one supplier has a NULL state. The NULL makes the NOT IN predicate unable to resolve TRUE. Diagnose the cause with `SELECT * FROM erp.suppliers WHERE state IS NULL;`. section 2.1

Use NOT IN safely when:

  • The excluded values are constants and the compared value is not unexpectedly NULL. section 2.1 section 2.1.2
  • The subquery is guaranteed not to return NULL values; otherwise prefer NOT EXISTS. section 2.1.2
  • You have checked scale and the execution plan, because NOT IN subqueries may switch from a fast hashed subplan to a much slower plain subplan. section 2.1.1
Listing 2.1 Example queries with IN/NOT IN
SELECT * FROM (VALUES ('Harper', 'salesperson'),
                      ('Parker', 'driver'),
                      ('Riley', 'electrician'),
                      ('Skyler', 'manager'))
              AS t(first_name, job_title)
WHERE job_title IN ('salesperson', 'driver', 'electrician');
 first_name |  job_title
------------+-------------
 Harper     | salesperson
 Parker     | driver
 Riley      | electrician
(3 rows)


SELECT * FROM (VALUES ('Harper', 'salesperson'),
                      ('Parker', 'driver'),
                      ('Riley', 'electrician'),
                      ('Skyler', 'manager'))
              AS t(first_name, job_title)
WHERE job_title NOT IN ('manager');
 first_name |  job_title
------------+-------------
 Harper     | salesperson
 Parker     | driver
 Riley      | electrician
(3 rows)

Compare ordinary IN and NOT IN with constant values before applying the predicate to nullable subquery results. section 2.1

For relational exclusion, rewrite the supplier query with NOT EXISTS and guard the outer value: `WHERE NOT EXISTS (SELECT FROM suppliers s WHERE ccd.state = s.state) AND ccd.state IS NOT NULL`. PostgreSQL can plan this form as a hash anti-join, producing predictable exclusion and a more scalable strategy. section 2.1.2

How PostgreSQL Mistakes and How to Avoid Them covers it

How deep it goes
Explains predicate behavior, NULL failures, diagnosis, plan changes, and NOT EXISTS and anti-join alternatives.
Examples
Demonstrates constant lists, supplier states, NULL-state diagnosis, customer emails, and EXPLAIN output.
What it assumes
Assumes basic SQL predicates, WHERE clauses, subqueries, and NULL awareness; reinforces predicate and anti-join terminology.
What you can do after
Diagnose NULL-sensitive exclusions, choose NOT EXISTS, and inspect PostgreSQL plans for scalability.
How it connects
Connects negative predicates with NULL, NOT EXISTS, anti-joins, subqueries, and query plans.

Also explained in

Compare how 2 books explain NOT IN →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of PostgreSQL Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.