Error 21000 more than one row returned by a subquery used as an expression

Подзапрос вернул более одной строки

Бывает так, что подзапрос одиночной строки возвращает более одной строки. В таком случае возникнет ошибка.

Для каждого магазина найдем одного сотрудника с должностью 'MANAGER'.

SELECT s.store_id,
       s.name,
       (SELECT e.employee_id
          FROM employee e
         WHERE e.store_id = s.store_id
           AND e.rank_id = 'MANAGER'
       ) AS employee_id
  FROM store s
 ORDER BY s.store_id
error: more than one row returned by a subquery used as an expression

Посмотрим, что там с данными не так?

SELECT e.store_id,
       e.rank_id,
       e.last_name || ' ' || e.first_name AS full_name 
  FROM employee e
 WHERE e.rank_id = 'MANAGER'
 ORDER BY e.store_id, e.last_name, e.first_name

В магазине 201 два менеджера, а в магазине 600 — три.

В такой ситуации есть два варианта развития событий. Мы можем решать неправильную задачу. Если в магазине может быть несколько менеджеров, то мы должны работать с массивом менеджеров. В таком случае может быть приемлемым получить одного из них, например, первого по алфавиту.

Возможно, менеджер в магазине должен быть только один. Просто кто-то не создал уникальный ключ, и пользователи создали несколько менеджеров в магазине. В таком случае, мы написали правильный запрос и необходимо избавляться от неправильных данных и дорабатывать структуру данных.

В любом случае, необходимо гарантировать, что в результате подзапроса будет возвращено не более одной строки. Для этого необходимо одно из:

  • должен быть уникальный ключ, гарантирующий, что в результате подзапроса будет не более одной строки;
  • использовать агрегатную функцию;
  • использовать LIMIT 1 для ограничения количества строк.

Воспользуемся LIMIT 1:

SELECT s.store_id,
       s.name,
       (SELECT e.employee_id
          FROM employee e
         WHERE e.store_id = s.store_id
           AND e.rank_id = 'MANAGER'
         ORDER BY e.last_name,
                  e.first_name,
                  e.middle_name
         LIMIT 1
       ) AS employee_id
  FROM store s
 ORDER BY s.store_id

P.S. Если нам нужен список ФИО, то можно воспользоваться string_agg:

SELECT s.store_id,
       s.name,
       (SELECT string_agg (
                  e.last_name || ' ' || e.first_name, '; '
                  ORDER BY e.last_name,
                           e.first_name
               )
          FROM employee e
         WHERE e.store_id = s.store_id
           AND e.rank_id = 'MANAGER'
       ) AS employees
  FROM store s
 ORDER BY s.store_id

Таких функций в PostgreSQL довольно много, и они заслуживают отдельной темы.

Symptom

When connected to Microsoft SQL Server using the OLEDB database driver , and running the following sql:

select *, (select (value_2) from table2 where table2.value_1 = table1.value_1) from table1

PowerBuilder will return the following error:

Select Error: SQLState=21000

Subquery returned more than 1 value. This is not permitted when the subquery follows

=, !=, < , , < =, > , > = or when the subquery is used as an expression

When connected to Microsoft SQL Server using SNC, a resultset will be returned. No error is returned.

Environment

 PowerBuilder

Microsoft SQL Server

Reproducing the Issue

1.      In PowerBuilder, create a new database profile for OLEDB connecting to Microsoft SQL Server.

2.      Run the following sql:

create table dbo.table1 (tbl1_ident int identity, value_1 varchar(20));

create table dbo.table2 (tbl2_ident int identity, value_1 varchar(20), value_2 varchar(1));

insert into dbo.table1 (value_1) values (‘row_1’) ;

insert into dbo.table1 (value_1) values (‘row_2’);

insert into table2 (value_1, value_2) values (‘row_1’, ‘1’);

insert into table2 (value_1, value_2) values (‘row_2’, ‘2’);

insert into table2 (value_1, value_2) values (‘row_2’, ‘3’);

insert into table2 (value_1, value_2) values (‘row_2’, ‘4’);

insert into table2 (value_1, value_2) values (‘row_2’, ‘5’);

3.      Open an existing workspace and target.

4.      Create a new tabular datawindow using the following sql:

select *, (select (value_2) from table2 where table2.value_1 = table1.value_1) from table1

5.       Retrieve the datawindow and you will see the following error when connected to

OLEDB:

Select Error: SQLState=21000

Subquery returned more than 1 value. This is not permitted when the subquery follows

=, !=, < , , < =, > , > = or when the subquery is used as an expression

6.       Now connect using the SNC database profile and retrieve the same datawindow and you should see a result set.

Cause

The PowerBuilder OLEDB driver and SNC driver fetch a different number of rows each time.

(The SNC database driver would returned the same error if the dbparm Block=1 is used.)

Resolution

For OLEDB, change the SQL from:

select *, (select (value_2) from table2 where table2.value_1 = table1.value_1) from

table1

to:

select top 1 *, (select (value_2) from table2 where table2.value_1 = table1.value_1) from

table1

RRS feed

  • Remove From My Forums
  • Вопрос

  • Can anyone tell me why I am getting this error when I try to view my report:

    Query Engine Error: ‘21000:[Microsoft][ODBC SQL Server Driver][SQL Sever] Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <=,>,>= or when the subquery is used as an

Все ответы

  • Hi there…looks like one of the queries being executed to retrieve data for the report contains an invalid use of a subquery that returns more than 1 record in a scalar context.  For example, something like what follows below is illegel if tableb contains more than one row, since the subquery is being used in/expected to act in scalar context (i.e. a single column, single row result set):

    select cola from tablea where colb = (select colb from tableb)

    If you have the query, maybe we could help some more…

sql - Postgres Error: More than one row returned by a subquery used as an expression

Technically, to repair your statement, you can add LIMIT 1 to the subquery to ensure that at most 1 row is returned. That would remove the error, your code would still be nonsense.

... SELECT store_key FROM store LIMIT 1 ...

Practically, you want to match rows somehow instead of picking an arbitrary row from the remote table store to update every row of your local table customer.
Your rudimentary question doesnt provide enough details, so I am assuming a text column match_name in both tables (and UNIQUE in store) for the sake of this example:

... SELECT store_key FROM store
     WHERE match_name =  || quote_literal(customer.match_name)  ...

But thats an extremely expensive way of doing things.

Ideally, you completely rewrite the statement.

UPDATE customer c
SET    customer_id = s.store_key
FROM   dblink(port=5432, dbname=SERVER1 user=postgres password=309245
            , SELECT match_name, store_key FROM store)
       AS s(match_name text, store_key integer)
WHERE c.match_name = s.match_name
AND   c.customer_id IS DISTINCT FROM s.store_key;

This remedies a number of problems in your original statement.

Obviously, the basic problem leading to your error is fixed.

Its typically better to join in additional relations in the FROM clause of an UPDATE statement than to run correlated subqueries for every individual row.

When using dblink, the above becomes a thousand times more important. You do not want to call dblink() for every single row, thats extremely expensive. Call it once to retrieve all rows you need.

With correlated subqueries, if no row is found in the subquery, the column gets updated to NULL, which is almost always not what you want. In my updated query, the row only gets updated if a matching row is found. Else, the row is not touched.

Normally, you wouldnt want to update rows, when nothing actually changes. Thats expensively doing nothing (but still produces dead rows). The last expression in the WHERE clause prevents such empty updates:

     AND   c.customer_id IS DISTINCT FROM sub.store_key

Related:

  • How do I (or can I) SELECT DISTINCT on multiple columns?

The fundamental problem can often be simply solved by changing an = to IN, in cases where youve got a one-to-many relationship. For example, if you wanted to update or delete a bunch of accounts for a given customer:

WITH accounts_to_delete AS 
    ( 
        SELECT     account_id
        FROM       accounts a
        INNER JOIN customers c
                ON a.customer_id = c.id
        WHERE      c.customer_name=Some Customer
    )

-- this fails if Some Customer has multiple accounts, but works if theres 1:
DELETE FROM accounts
 WHERE accounts.guid = 
( 
    SELECT account_id 
    FROM   accounts_to_delete 
);

-- this succeeds with any number of accounts:
DELETE FROM accounts
 WHERE accounts.guid IN   
( 
    SELECT account_id 
    FROM   accounts_to_delete 
);

sql – Postgres Error: More than one row returned by a subquery used as an expression

This means your nested SELECT returns more than one rows.

You need to add a proper WHERE clause to it.

Related posts on SQL :

  • mysql – Error: Duplicate entry 0 for key PRIMARY
  • sql – snowflake substring by pattern
  • Optimize Temporary Table on Presto/Hive SQL
  • Node.js mysql transaction
  • sql – Redshift ROUND function doesnt round in some cases?
  • mysql – OpenSSL – error 18 at 0 depth lookup:self signed certificate
  • postgresql – password authentication failed for user postgres
  • sql – Update Query with Nolock hint

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Error 210 the elder scrolls online
  • Error 210 cpu variable mtrr configure failed что делать
  • Error 21 устройство не готово getting the device geometry ultraiso
  • Error 21 при прошивке андроид
  • Error 21 selected disk does not exist что делать

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии