A subquery is a query nested inside another query — SQL runs the inner query first, then uses its result as part of the outer query's logic. They let you answer questions that depend on a value you don't know ahead of time, like "who earns more than the average salary," without hardcoding that average yourself.

Subqueries can appear in a WHERE clause to filter rows, or in a FROM clause to act as a temporary, on-the-fly table.

A Scalar Subquery in WHERE

A "scalar" subquery returns exactly one value, which makes it safe to compare directly with =, >, or <. Here's everyone earning above the company-wide average:

SELECT name, salary
FROM employees
WHERE salary > (
    SELECT AVG(salary) FROM employees
);

Result:

name | salary
--------------|-------
Carla Gomez | 95000
David Kim | 95000
Elena Petrov | 88000
Henry Adams | 71000

The inner query runs once, computes 73000, and the outer query then filters against that number as if it had been typed in directly.

A Multi-Row Subquery With IN

When a subquery can return several rows, pair it with IN instead of a direct comparison operator:

SELECT name, department
FROM employees
WHERE department IN (
    SELECT department
    FROM employees
    GROUP BY department
    HAVING COUNT(*) > 2
);

Result:

name | department
--------------|------------
Carla Gomez | Engineering
David Kim | Engineering
Elena Petrov | Engineering

This finds every employee who works in a department with more than two people — the inner query figures out which departments qualify, and the outer query pulls the matching employees.

A Subquery in FROM

A subquery can also stand in for a table, letting you query a result set instead of raw data. This is handy when you need to filter or sort after aggregating:

SELECT department, total_salary
FROM (
    SELECT department, SUM(salary) AS total_salary
    FROM employees
    GROUP BY department
) AS department_totals
WHERE total_salary > 150000;

Every subquery used this way needs an alias (AS department_totals here) — SQL requires a name for any derived table. When a query like this starts feeling hard to read, a Common Table Expression (CTE) is usually a cleaner way to write the same logic.