Skip to main content

Subqueries

A subquery is a query inside another query. Use subqueries to filter, compare, or compute values that depend on data from the main query.

Quick Start

-- Find employees earning above department average
SELECT name, salary, department
FROM employees
WHERE salary > (
SELECT AVG(salary)
FROM employees AS e2
WHERE e2.department = employees.department
);

Result: Employees who earn more than their department's average salary.

Types of Subqueries

1. Scalar Subqueries (Single Value)

-- Compare to overall average
SELECT name, salary,
(SELECT AVG(salary) FROM employees) AS company_avg
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);

Returns: Single value (one row, one column).

2. Table Subqueries (Multiple Rows)

-- Filter by department conditions
SELECT name, department
FROM employees
WHERE department IN (
SELECT name
FROM departments
WHERE budget > 500000
);

Returns: Multiple rows, used with IN, EXISTS, ANY, ALL.

Subquery Placement

WHERE Clause - Filtering

-- Employees in high-budget departments
SELECT name, salary
FROM employees
WHERE department IN (
SELECT name FROM departments WHERE budget > 500000
);

FROM Clause - Data Source

-- Analyze high earners by department
SELECT department, AVG(salary) as avg_salary
FROM (
SELECT * FROM employees WHERE salary > 70000
) AS high_earners
GROUP BY department;

SELECT Clause - Computed Columns

-- Show salary vs department average
SELECT name, salary,
(SELECT AVG(salary)
FROM employees e2
WHERE e2.department = e1.department) as dept_avg
FROM employees e1;

Correlated vs Uncorrelated

Uncorrelated - Independent

-- Same subquery runs once
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);

Performance: Subquery executes once, result reused.

Correlated - Dependent

-- Subquery runs for each main row
SELECT name, salary, department
FROM employees e1
WHERE salary > (
SELECT AVG(salary)
FROM employees e2
WHERE e2.department = e1.department
);

Performance: Subquery executes for each outer row.

Common Patterns

-- Employees who have projects
SELECT name, department
FROM employees e
WHERE EXISTS (
SELECT 1 FROM projects p WHERE p.employee_id = e.id
);

NOT EXISTS - Check for Missing Data

-- Employees without projects
SELECT name, department
FROM employees e
WHERE NOT EXISTS (
SELECT 1 FROM projects p WHERE p.employee_id = e.id
);

ANY/ALL - Multiple Comparisons

-- Employees earning more than ANY marketing employee
SELECT name, salary
FROM employees
WHERE salary > ANY (
SELECT salary FROM employees WHERE department = 'Marketing'
);

-- Employees earning more than ALL marketing employees
SELECT name, salary
FROM employees
WHERE salary > ALL (
SELECT salary FROM employees WHERE department = 'Marketing'
);

When to Use Subqueries vs JOINs

✅ Use Subqueries when:

  • Filtering based on aggregate conditions
  • Checking existence/non-existence
  • Need computed values in SELECT
  • Logic is clearer as nested steps

✅ Use JOINs when:

  • Need columns from multiple tables
  • Better performance for large datasets
  • Combining data rather than filtering