A subquery is a SELECT statement nested inside another SQL statement to filter, project, or derive data.
Use subqueries to nest SELECT statements inside another query for filtering rows, calculating values, and building derived tables.
A subquery, also called an inner query, is a SELECT statement enclosed in parentheses that appears in SELECT, FROM, WHERE, or HAVING clauses of an outer query.PostgreSQL executes the subquery first, then feeds its result to the outer query.
(SELECT column_list FROM table_name WHERE conditions)
Place the parentheses exactly as shown.Alias the subquery when it appears in the FROM clause so the outer query can reference its columns.
Use scalar subqueries to compute single values, like totals or flags, for every row returned by the outer query.
Use a subquery as a temporary table to simplify complex joins or aggregation pipelines.Always give it an alias.
Filter rows with IN, EXISTS, = ANY (array comparators), or comparison operators. Correlated subqueries here can reference outer-query columns.
Include a reference to a column from the outer query inside the subquery’s WHERE clause.PostgreSQL runs the subquery once per outer-query row.
Index the columns used to correlate or filter, prefer EXISTS over IN for large result sets, and consider rewriting to JOINs when row counts explode.Analyze the plan with EXPLAIN.
Keep subqueries small, alias derived tables, return only needed columns, and move expensive calculations out of correlated subqueries when possible.
Mistake 1 – Unaliased derived table: Every subquery in FROM must have an alias.Fix: add AS alias
.
Mistake 2 – Subquery returns multiple rows where scalar expected: Use IN
/= ANY
or add aggregation/limit to force a single row.
.
Not inherently. PostgreSQL’s planner can often transform subqueries into JOINs. Still, correlated subqueries may run once per outer row, so test with EXPLAIN and compare to JOIN rewrites.
Yes. PostgreSQL supports arbitrary nesting depth, limited only by statement complexity and memory. Keep logic readable by refactoring into CTEs when depth grows.
EXISTS stops on the first match and performs better when the inner set is large and not indexed uniquely. IN can be faster for small, static lists returned by the subquery.