How to Use EXISTS in SQL
Use EXISTS when you only need to know whether at least one matching row exists in another query. This is perfect for fast relationship checks, conditional filters, and correlated existence logic.
What you’ll build or solve
You’ll learn how to use EXISTS in SQL to filter rows based on matching related data. You’ll also know when it is clearer than IN.
Learn SQL on Mimo
When this approach works best
This approach is the right choice when the question is simply “does at least one related row exist?”
Common real-world scenarios include:
- Users with orders
- Products with reviews
- Customers with unpaid invoices
- Students with enrollments
- Accounts with failed logins
This is a bad idea when the full related values themselves are needed in the result. In that case, use JOIN.
Prerequisites
You only need:
- Basic
SELECTand subquery knowledge - Two logically related tables
Step-by-step instructions
Step 1: Add an EXISTS subquery in WHERE
The inner query usually references the outer row.
SQL
SELECT *
FROM users u
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.user_id = u.user_id
);
This returns only users who have at least one order.
The inner query stops as soon as it finds one match.
This often makes EXISTS efficient for large relationship checks.
What to look for:
- Returns rows only when a match exists
- Great for correlated relationship checks
- Stops on first match
- Often clearer than
IN - Use
NOT EXISTSfor missing relationships
Examples you can copy
Products with reviews
SQL
SELECT *
FROM products p
WHERE EXISTS (
SELECT 1
FROM reviews r
WHERE r.product_id = p.product_id
);
Customers with unpaid invoices
SQL
SELECT *
FROM customers c
WHERE EXISTS (
SELECT 1
FROM invoices i
WHERE i.customer_id = c.customer_id
AND i.status = 'unpaid'
);
Students with enrollments
SQL
SELECT *
FROM students s
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE e.student_id = s.student_id
);
Common mistakes and how to fix them
Mistake 1: Forgetting the correlation key
What the reader might do:
SQL
WHERE EXISTS (
SELECT 1
FROM orders
)
Why it breaks: every outer row passes if the table has any row at all.
Corrected approach:
Reference the outer row inside the subquery.
Mistake 2: Using EXISTS when related values are needed
What the reader might do:
Filter users with orders, then later need order totals.
Why it breaks: EXISTS only checks presence.
Corrected approach:
Use JOIN when actual related fields are needed.
Mistake 3: Returning full columns in the subquery
What the reader might do:
SQL
SELECT *
FROM orders
Why it breaks: the selected inner columns do not matter.
Corrected approach:
Use SELECT 1 for clarity.
Troubleshooting
If every row returns, verify the correlation condition.
If performance is slow, index the related key.
If missing rows are needed instead, use NOT EXISTS.
If related columns are needed in output, switch to JOIN.
Quick recap
- Use
EXISTSfor existence checks - Correlate the inner query to the outer row
SELECT 1is the clearest pattern- Great for relationship filtering
- Use
NOT EXISTSfor missing matches
Join 35M+ people learning for free on Mimo
4.8 out of 5 across 1M+ reviews
Check us out on Apple AppStore, Google Play Store, and Trustpilot