SQL EXISTS, ANY and ALL
EXISTS asks a yes/no question: “is there at least one matching row?” It is the clearest and safest way to filter by the presence or absence of related data. ANY and ALL compare one value with every value a subquery returns: “more than any sales salary”, “more than all of them”.
- Filter with
EXISTSandNOT EXISTS - Know why
NOT EXISTSis safer thanNOT IN - Compare against a list with
ANY/SOME - Compare against every value with
ALL
EXISTS syntax
SELECT columnsFROM table_a aWHERE EXISTS (SELECT 1 FROM table_b b WHERE b.key = a.key);The subquery’s select list does not matter: EXISTS only checks whether any row comes back. SELECT 1 is the usual convention.
Sample data
| customer_id | customer_name | city |
|---|---|---|
| 1 | Ananya Sharma | Mumbai |
| 2 | Rohit Kumar | Delhi |
| 3 | Fatima Khan | Hyderabad |
| 4 | David Fernandes | Goa |
| 5 | Ishita Bose | Kolkata |
| 6 | Manoj Pillai | Chennai |
| 7 | Sara Thomas | Bengaluru |
| order_id | customer_id | order_date | status |
|---|---|---|---|
| 1001 | 1 | 2025-01-05 | Delivered |
| 1002 | 2 | 2025-01-17 | Delivered |
| 1003 | 1 | 2025-02-02 | Delivered |
| 1004 | 3 | 2025-02-14 | Cancelled |
| 1005 | 4 | 2025-03-08 | Delivered |
| 1006 | 5 | 2025-03-22 | Shipped |
| 1007 | 2 | 2025-04-10 | Delivered |
| 1008 | 1 | 2025-04-28 | Pending |
| 1009 | 3 | 2025-05-15 | Delivered |
| 1010 | 5 | 2025-06-01 | Delivered |
EXISTS: rows that have a match
Customers with at least one order:
SELECT c.customer_id, c.customer_nameFROM customers cWHERE EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id)ORDER BY c.customer_id;| customer_id | customer_name |
|---|---|
| 1 | Ananya Sharma |
| 2 | Rohit Kumar |
| 3 | Fatima Khan |
| 4 | David Fernandes |
| 5 | Ishita Bose |
- For each customer, the subquery looks for an order with the same
customer_id. - As soon as one is found,
EXISTSis true; MySQL does not need to look for more. - Five customers have orders. Unlike a join, a customer with three orders still appears once.
NOT EXISTS: rows without a match
Customers who have never ordered:
SELECT c.customer_id, c.customer_nameFROM customers cWHERE NOT EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);| customer_id | customer_name |
|---|---|
| 6 | Manoj Pillai |
| 7 | Sara Thomas |
Add conditions inside the subquery for more specific questions. Customers with no delivered order:
SELECT c.customer_nameFROM customers cWHERE NOT EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id AND o.status = 'Delivered')ORDER BY c.customer_name;| customer_name |
|---|
| Manoj Pillai |
| Sara Thomas |
NOT EXISTS vs NOT IN with NULLs
Who manages nobody? With NOT IN and a subquery that contains a NULL, the answer is silently empty:
SELECT first_nameFROM employeesWHERE emp_id NOT IN (SELECT manager_id FROM employees);NOT EXISTS gives the right answer, because it only checks for matching rows:
SELECT e.first_nameFROM employees eWHERE NOT EXISTS (SELECT 1 FROM employees r WHERE r.manager_id = e.emp_id)ORDER BY e.emp_id;| first_name |
|---|
| Priya |
| Karan |
| Neha |
| Arjun |
| Rohan |
| Sneha |
| Amit |
- For each employee
e, the subquery looks for anyone whosemanager_idise.emp_id. - The CEO’s NULL
manager_idsimply never matches; it cannot poison the result. - Seven employees have no direct reports.
For “rows with no match in another table”, use NOT EXISTS (or LEFT JOIN ... IS NULL). Avoid NOT IN unless the column is declared NOT NULL.
ANY / SOME
value > ANY (subquery) is true if the value is greater than at least one value in the list. Employees who earn more than at least one sales employee:
SELECT first_name, salaryFROM employeesWHERE salary > ANY (SELECT salary FROM employees WHERE dept_id = 2) AND dept_id <> 2ORDER BY salary;| first_name | salary |
|---|---|
| Priya | 72000 |
| Karan | 72000 |
| Sneha | 78000 |
| Kavya | 88000 |
| Rahul | 98000 |
| Asha | 125000 |
- The subquery returns the Sales salaries: 105,000, 54,000 and 48,000.
> ANYmeans “more than the smallest of them”, here more than 48,000.= ANYis the same asIN.SOMEis a synonym forANY.
ALL
value > ALL (subquery) is true only if the value is greater than every value in the list:
SELECT first_name, salaryFROM employeesWHERE salary > ALL (SELECT salary FROM employees WHERE dept_id = 2)ORDER BY salary;| first_name | salary |
|---|---|
| Asha | 125000 |
| Meera | 250000 |
> ALLmeans “more than the largest”, here more than 105,000.- Only Asha (125,000) and Meera (250,000) qualify.
<> ALLis the same asNOT IN, including its NULL problem.
| Expression | Equivalent |
|---|---|
x > ANY (list) | x > MIN(list) |
x > ALL (list) | x > MAX(list) |
x = ANY (list) | x IN (list) |
x <> ALL (list) | x NOT IN (list) |
Exercise: Products that were ordered
List the products that appear in at least one order line, using EXISTS.
| product_name |
|---|
| Wireless Mouse |
| Mechanical Keyboard |
| USB-C Hub |
| Office Chair |
| Standing Desk |
| Notebook Pack |
| Gel Pens |
| Desk Lamp |
Hint 1
Correlate order_items.product_id with products.product_id.
SELECT p.product_nameFROM products pWHERE EXISTS (SELECT 1 FROM order_items oi WHERE oi.product_id = p.product_id)ORDER BY p.product_id;| product_name |
|---|
| Wireless Mouse |
| Mechanical Keyboard |
| USB-C Hub |
| Office Chair |
| Standing Desk |
| Notebook Pack |
| Gel Pens |
| Desk Lamp |
Exercise: Departments without staff
List departments that have no employees, using NOT EXISTS.
| dept_name |
|---|
| Finance |
Hint 1
Look for an employee whose dept_id matches the department.
SELECT d.dept_nameFROM departments dWHERE NOT EXISTS (SELECT 1 FROM employees e WHERE e.dept_id = d.dept_id);| dept_name |
|---|
| Finance |
Quick quiz
1. What does EXISTS check?
EXISTS is true as soon as one row is found.
2. Why is NOT EXISTS safer than NOT IN?
A NULL in a NOT IN list makes every comparison UNKNOWN.
3. salary > ALL (10, 20, 30) is true for…
It must be greater than every value, so greater than 30.
EXISTS FAQ
What is the difference between EXISTS and IN?
IN compares a value with a list returned by the subquery. EXISTS only checks whether a correlated subquery returns any row. They often give the same result, but NOT EXISTS handles NULLs correctly where NOT IN does not.
Is EXISTS faster than IN?
In MySQL 8 the optimizer turns both into semi-joins in most cases, so performance is usually similar. Choose the one that reads best; prefer NOT EXISTS over NOT IN.
What does SELECT 1 mean inside EXISTS?
Nothing special: EXISTS ignores the selected values. SELECT 1 just shows that only the presence of a row matters.