datarekha
SQL Easy Asked at GoogleAsked at Microsoft

What is a scalar subquery, where can it appear in a SQL statement, and what happens if it returns more than one row?

The short answer

A scalar subquery is a subquery that returns exactly one column and one row, and can appear anywhere a single value is valid — SELECT list, WHERE clause, HAVING clause, or even a JOIN ON condition. If it returns more than one row at runtime the database raises a runtime error, not a compile-time error.

How to think about it

“Scalar” just means a single value. A scalar subquery reduces to exactly one column and one row, so it slots in anywhere you’d otherwise write a literal number or string — the SELECT list, a WHERE predicate, a HAVING clause, even a JOIN ON. The interesting parts are where it can appear and what breaks it.

A worked example — scalar subquery in SELECT and WHERE

Here the same AVG(salary) subquery does double duty: it attaches the company average to every row and filters to above-average earners:

SELECT name, dept_id, salary,
       ROUND((SELECT AVG(salary) FROM employees)) AS company_avg,
       salary - ROUND((SELECT AVG(salary) FROM employees)) AS vs_avg
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees)
ORDER BY salary DESC;
namedept_idsalarycompany_avgvs_avg
Dave212000094167.025833.0
Eve211000094167.015833.0
Alice19500094167.0833.0

Three above-average earners; company_avg is the same scalar (94167) repeated on each row, and vs_avg is each salary’s distance from it. Notice the subquery is written three times here — twice in SELECT, once in WHERE — and the engine evaluates it on each reference. A CTE or window function computes it once. (ROUND returns a float in SQLite, hence the .0.)

The “more than one row” runtime error

The database can’t know at parse time whether a correlated subquery will return one row or many — it depends on the data. If it returns more than one at runtime:

ERROR: more than one row returned by a subquery used as an expression

This only fires when a match exists, so a query that’s fine in staging can blow up in production as the data grows. LIMIT 1 is the usual patch, but it silently discards ties — which may not be what you want. For per-row lookups like “top earner per department,” a window function is cleaner and lets the optimiser scan once:

SELECT department_id, name, salary
FROM (
  SELECT department_id, name, salary,
         RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rk
  FROM employees
) ranked
WHERE rk = 1;
Learn it properly Subqueries

Keep practising

All SQL questions

Explore further

Skip to content