Skip to content
QueryLab

SQL subqueries

A subquery is a query used as a value, a table, or a yes-or-no test. Use it when the comparison itself has to be computed from the same tables.

Scalar, table, and EXISTS

A scalar subquery returns one value, such as the average current salary. A table subquery belongs in FROM or JOIN and should usually be a CTE once it grows. EXISTS asks whether any row satisfies a condition, which is the right tool for retention.

Correlation

A correlated subquery refers to the outer row. The latest salary pattern uses MAX(effective_date) WHERE employee_id equals the employee currently being considered. It runs conceptually once per employee.

Example

SELECT e.name
FROM employees AS e
JOIN salaries AS s ON s.employee_id = e.id
WHERE s.effective_date = (
  SELECT MAX(s2.effective_date)
  FROM salaries AS s2
  WHERE s2.employee_id = e.id
);

The inner query sees e.id from the outer row. That is the correlation.

Practice

Common mistakes

  • A scalar subquery that returns more than one row.
  • Filtering history with a global MAX instead of a per-employee MAX.
  • Using IN on a nullable column and being surprised by the three-valued logic.
  • Repeating a long subquery instead of naming it with a CTE.

FAQ

When is a CTE better than a subquery?
When you reference the intermediate result more than once, or when a reviewer needs a name for the business rule. Customer spend and workspace cohorts in QueryLab are written that way.
Is EXISTS faster than IN?
Often similar after planning. EXISTS is clearer when you only care that a match exists, and it avoids pulling a column you will not display.

Related practice

Start solving SQL challenges today.

Open Above-average current salaries and check the result in the browser. An account is optional.

Start practicing free