Glossary/SQL Subqueries — Scalar, Correlated, Row, Table & Nested
SQL & Databases

SQL Subqueries — Scalar, Correlated, Row, Table & Nested

Queries inside queries — the power tool for complex data retrieval.


Definition

A subquery (inner query or nested query) is a SELECT statement embedded inside another SQL statement. Subqueries can appear in SELECT, FROM, WHERE, and HAVING clauses. Types: scalar subquery (returns one value), column subquery (returns one column), row subquery (returns one row), table subquery (returns a full result set), and correlated subquery (references outer query — re-executes per outer row). Subqueries are the building blocks of complex analytical queries.

Real-life analogy: The answer within an answer

A subquery is like asking: 'Show me all employees who earn more than the average salary' — but you first need to know the average salary before you can filter. You ask one question (what is the average?) and use that answer inside another question (who earns more than that?). Subqueries let you compute answers from your data and use them immediately in the same query.

Types of subqueries — by return value

All subquery types with use cases

-- 1. SCALAR SUBQUERY: returns exactly ONE value (one row, one column)
-- Used anywhere a single value is expected
SELECT
    Name,
    Salary,
    (SELECT AVG(Salary) FROM Employee) AS CompanyAvg,  -- In SELECT list
    Salary - (SELECT AVG(Salary) FROM Employee) AS DiffFromAvg
FROM Employee
WHERE Salary > (SELECT AVG(Salary) FROM Employee);      -- In WHERE

-- 2. COLUMN SUBQUERY: returns ONE column (multiple rows)
-- Used with IN, ANY, ALL operators
SELECT Name FROM Employee
WHERE DeptID IN (
    SELECT DeptID FROM Department WHERE Budget > 1000000
);

-- ANY: true if condition is true for AT LEAST ONE value
SELECT Name FROM Employee
WHERE Salary > ANY (SELECT Salary FROM Employee WHERE DeptID = 2);
-- Returns employees earning more than the MINIMUM salary in Dept 2

-- ALL: true if condition is true for ALL values
SELECT Name FROM Employee
WHERE Salary > ALL (SELECT Salary FROM Employee WHERE DeptID = 2);
-- Returns employees earning more than the MAXIMUM salary in Dept 2

-- 3. ROW SUBQUERY: returns exactly ONE row
SELECT * FROM Employee
WHERE (DeptID, JobTitle) = (SELECT DeptID, JobTitle FROM Employee WHERE EmpID = 101);
-- Find all employees with same dept AND job title as EmpID 101

-- 4. TABLE SUBQUERY (Derived Table / Inline View): returns a full result set
-- Used in FROM clause — must be given an alias
SELECT dept_stats.DeptName, dept_stats.AvgSalary, dept_stats.HeadCount
FROM (
    SELECT d.DeptName, AVG(e.Salary) AS AvgSalary, COUNT(*) AS HeadCount
    FROM Employee e JOIN Department d ON e.DeptID = d.DeptID
    GROUP BY d.DeptName
) AS dept_stats                        -- Alias required for derived table
WHERE dept_stats.HeadCount > 5
ORDER BY dept_stats.AvgSalary DESC;

-- 5. CORRELATED SUBQUERY: references outer query — runs once per outer row
-- Find employees earning above their OWN department average
SELECT e.Name, e.Salary, e.DeptID
FROM Employee e
WHERE e.Salary > (
    SELECT AVG(Salary) FROM Employee
    WHERE DeptID = e.DeptID    -- References outer query: e.DeptID
);

-- Correlated with EXISTS: find departments that have at least one manager
SELECT d.DeptName FROM Department d
WHERE EXISTS (
    SELECT 1 FROM Employee e
    WHERE e.DeptID = d.DeptID AND e.JobTitle = 'Manager'
);

Nested subqueries and alternatives

Multi-level nesting and refactoring with CTEs

-- Deeply nested subquery (hard to read)
SELECT Name FROM Employee
WHERE DeptID IN (
    SELECT DeptID FROM Department
    WHERE ManagerID IN (
        SELECT EmpID FROM Employee
        WHERE Salary > (SELECT AVG(Salary) * 1.5 FROM Employee)
    )
);
-- Complex, hard to debug — good candidate for CTE refactoring

-- Same query refactored with CTEs (much more readable)
WITH
TopEarners AS (
    SELECT EmpID FROM Employee
    WHERE Salary > (SELECT AVG(Salary) * 1.5 FROM Employee)
),
WellPaidMgrDepts AS (
    SELECT DeptID FROM Department
    WHERE ManagerID IN (SELECT EmpID FROM TopEarners)
)
SELECT Name FROM Employee
WHERE DeptID IN (SELECT DeptID FROM WellPaidMgrDepts);

-- Subquery in UPDATE
UPDATE Employee
SET Salary = Salary * 1.10
WHERE DeptID = (
    SELECT DeptID FROM Department WHERE DeptName = 'Engineering'
);

-- Subquery in DELETE
DELETE FROM Orders
WHERE CustomerID IN (
    SELECT CustomerID FROM Customers WHERE Status = 'Blacklisted'
);

-- Subquery vs JOIN performance comparison:
-- These often produce the same result:
-- Using subquery:
SELECT Name FROM Employee
WHERE DeptID IN (SELECT DeptID FROM Department WHERE Location = 'Mumbai');
-- Using JOIN (often faster for large datasets):
SELECT DISTINCT e.Name FROM Employee e
JOIN Department d ON e.DeptID = d.DeptID
WHERE d.Location = 'Mumbai';
Subquery typeLocationReturnsExample use
ScalarSELECT, WHERE, HAVINGSingle valueCompare salary to company average
ColumnWHERE with IN/ANY/ALLSingle column, multiple rowsFilter by list of valid IDs
RowWHERE with tuple comparisonSingle rowMatch on multiple columns simultaneously
Table (Derived)FROM clauseFull result setPre-aggregate then filter on aggregates
CorrelatedWHERE, HAVING, SELECTVaries — re-runs per outer rowPer-row comparisons (above dept average)

Practice questions

  1. Find the employee with the highest salary using a subquery. (Answer: SELECT * FROM Employee WHERE Salary = (SELECT MAX(Salary) FROM Employee) — scalar subquery returning the maximum salary.)
  2. What makes a correlated subquery different from an uncorrelated one? (Answer: Correlated subquery references a column from the outer query. It re-executes once per row of the outer query — O(n) executions. Uncorrelated subquery runs once independently of the outer query — O(1) execution.)
  3. WHERE Salary > ANY (SELECT Salary FROM Employee WHERE DeptID = 2) — what does this return? (Answer: Employees whose salary is greater than the MINIMUM salary in Dept 2. ANY = true if condition holds for at least one value. > ANY is equivalent to > MIN.)
  4. Can you use a column alias defined in the same SELECT in a derived table subquery? (Answer: Yes — derived table (subquery in FROM) computes its result first, so the outer query can reference the alias. But you cannot reference aliases in the same SELECT's WHERE clause.)
  5. Rewrite: SELECT Name FROM Employee WHERE DeptID IN (SELECT DeptID FROM Department WHERE Budget > 1M) using JOIN. (Answer: SELECT DISTINCT e.Name FROM Employee e JOIN Department d ON e.DeptID = d.DeptID WHERE d.Budget > 1000000)

On LumiChats

LumiChats can detect when a correlated subquery can be replaced with a more efficient JOIN, refactor deeply nested queries into readable CTEs, and explain query performance differences. Great for SQL code reviews.

Try it free

Try LumiChats for ₹69

39+ AI models. Study Mode with page-locked answers. Agent Mode with code execution. Pay only on days you use it.

Get Started — ₹69/day

Related Terms

4 terms