EXISTS vs IN — Choosing the Right Subquery Pattern
10 min read
EXISTS and IN
Both EXISTS and IN let you filter rows based on a related subquery. They often produce the same results, but they differ in how they work — and in some situations one is significantly faster than the other.
IN with a Subquery
IN evaluates the subquery, collects all its values into a list, and checks whether the outer row's column appears in that list. It is intuitive and readable.
sql
-- Students enrolled in course 1
SELECT name FROM students
WHERE id IN (
SELECT student_id FROM enrolments WHERE course_id = 1
);EXISTS
EXISTS checks whether a *correlated* subquery returns at least one row. It stops searching as soon as the first match is found, which can make it faster when the inner table is large.
sql
-- Same result with EXISTS
SELECT name FROM students s
WHERE EXISTS (
SELECT 1 FROM enrolments e
WHERE e.student_id = s.id AND e.course_id = 1
);
-- Students who have NOT enrolled in anything
SELECT name FROM students s
WHERE NOT EXISTS (
SELECT 1 FROM enrolments e WHERE e.student_id = s.id
);Key Differences
INcan behave unexpectedly when the subquery returnsNULLvalues — the entireINcheck becomesNULL(unknown).NOT INwith NULLs silently returns no rows.EXISTSshort-circuits on the first match;INbuilds the full list first.- For large inner result sets,
EXISTSis often faster. For small lists,INis fine and more readable. - Modern query optimisers sometimes rewrite one into the other automatically.
Never use NOT IN (subquery) if the subquery might return NULL values. Use NOT EXISTS instead — it handles NULL correctly and avoids silently returning zero rows.
Ready to test yourself?
Practice SQL— quiz & coding exercises