Skip to content
SQL errors / Fix “More Than One Row Returned by a Subquery” in SQL
SQL error

Fix “More Than One Row Returned by a Subquery” in SQL

A subquery used as a single value found several. Use IN, or decide which one row you want.

The exact message
DuckDB
Invalid Input Error: More than one row returned by a subquery used as an expression - scalar subqueries can only return a single row.
PostgreSQL
more than one row returned by a subquery used as an expression
SQL Server
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
MySQL
Subquery returns more than 1 row

Error 1242.

Oracle
ORA-01427: single-row subquery returns more than one row
SQLite
No error - the first row is used.

Checked on SQLite 3.53. The other rows are ignored, so the answer can be silently wrong.

The symptom

A subquery sits where SQL expects one value: after = or >, or as a column in the SELECT list. That's a scalar subquery, and it may return one row or none. This one returned more, and the database can't choose between them for you.

The bench below looks for customers with a pending order. Garden Shop has three pending orders, so the subquery returns three customer IDs, and = has nothing single to compare with.

The fix: IN for a list

When any of the rows should count as a match, you wanted in, not =. It compares against the whole list:

The query that fails
select customer_id, first_name
from customers
where customer_id = (
  select customer_id
  from orders
  where status = 'pending'
)
order by customer_id;

Running it raises More than one row returned by a subquery used as an expression.

The corrected query
select customer_id, first_name
from customers
where customer_id in (
  select customer_id
  from orders
  where status = 'pending'
)
order by customer_id;

Open the corrected query in the playground

That returns Nadia, Owen, and Mia, the three customers with a pending order. The same goes for the other comparisons: > any (...) and > all (...) compare against a list where > can't.

When you need one value per row

In a SELECT list, a list won't do: each output row has one cell to fill. This asks for the product on each order, and most orders have more than one line item:

Wrong: one cell, several products
select o.order_id,
       (select oi.product_id
        from order_items as oi
        where oi.order_id = o.order_id) as product_id
from orders as o
order by o.order_id;

Decide which value you meant, and say so in the subquery. Every choice below returns one row per order.

Right: count them
select o.order_id,
       (select count(*)
        from order_items as oi
        where oi.order_id = o.order_id) as line_items
from orders as o
order by o.order_id
limit 5;
Right: list them in one value
select o.order_id,
       (select string_agg(p.product_name, ', ' order by p.product_name)
        from order_items as oi
        join products as p on p.product_id = oi.product_id
        where oi.order_id = o.order_id) as products
from orders as o
order by o.order_id
limit 5;
Right: pick the top one
select o.order_id,
       (select p.product_name
        from order_items as oi
        join products as p on p.product_id = oi.product_id
        where oi.order_id = o.order_id
        order by oi.quantity * oi.unit_price desc, p.product_name
        limit 1) as biggest_line
from orders as o
order by o.order_id
limit 5;

The tie-breaker after the sort matters: without it, two lines worth the same amount leave limit 1 free to return either. If you want every product as its own row, drop the subquery and join order_items instead.

Find the keys with more than one match

Run the subquery on its own and group by the column it matches on. Every group with a count above 1 is a row that breaks the scalar subquery:

Which orders have several line items?
select order_id, count(*) as line_items
from order_items
group by order_id
having count(*) > 1
order by line_items desc, order_id
limit 5;

Order 24 has three line items, and most others have two. When you expected one match per key, this is how you find the duplicates.

No rows is fine

A scalar subquery that finds no rows returns NULL without an error. Only too many rows fail.

Don't turn the check off

DuckDB's message ends with a suggestion: Use "SET scalar_subquery_error_on_multiple_rows=false" to revert to the previous behavior of returning a random row. That swaps an error for a wrong answer that looks right. The setting stays on for the rest of the connection, so every later query loses the check too (in the SQLShed playground, the rest of that Run). Fix the subquery instead.

How other engines word it

Engine Message
DuckDB Invalid Input Error: More than one row returned by a subquery used as an expression - scalar subqueries can only return a single row.
PostgreSQL more than one row returned by a subquery used as an expression
SQL Server Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
MySQL Subquery returns more than 1 row Error 1242.
Oracle ORA-01427: single-row subquery returns more than one row
SQLite No error - the first row is used. Checked on SQLite 3.53. The other rows are ignored, so the answer can be silently wrong.

SQLite behaves like DuckDB with the check turned off: it uses the first row and says nothing.

Related errors

Learn more