Rows missing in another table

The anti-join — customers with no orders, products never sold, records that failed to sync.

The problem

You want the rows in table A that have no match in table B. "Which customers have never ordered?" "Which products have zero sales?"

The pattern: LEFT JOIN … IS NULL

Join, then keep only the rows where the right side came back empty.

SQL playground
Loading editor…
⌘/Ctrl + Enter

Test the right table's column for NULL — ideally a non-nullable one like its primary key, so a legitimately-null column doesn't fake a "no match".

The pattern: NOT EXISTS

Usually the clearest, and the optimiser likes it:

SQL playground
Loading editor…
⌘/Ctrl + Enter

The pattern: NOT IN (careful)

SQL playground
Loading editor…
⌘/Ctrl + Enter

NOT IN is a trap: if the subquery returns a single NULL, the whole result is empty. NOT EXISTS doesn't have this problem. Only reach for NOT IN when the subquery column is guaranteed non-null.

Which to use

Handles NULLs safelyReadableUsually fastest
LEFT JOIN … IS NULLyes (test the PK)okyes
NOT EXISTSyesyesyes
NOT INnoyesvaries
  • LEFT JOIN — the join this pattern is built on.
  • WHERE — why NULL in a predicate is UNKNOWN, not FALSE.
  • NULL handling — the three-valued logic behind the NOT IN trap.

On this page