Ошибка Postgres: более чем одна строка, возвращаемая подзапросом, используемым как выражение

У меня есть две отдельные базы данных. Я пытаюсь обновить столбец в одной базе данных до значений столбца из другой базы данных:

UPDATE customer
SET customer_id=
   (SELECT t1 FROM dblink('port=5432, dbname=SERVER1 user=postgres password=309245',
   'SELECT store_key FROM store') AS (t1 integer));

Это ошибка, которую я получаю:

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

Есть идеи?

Ответы

Ответ 1

Технически, чтобы восстановить ваше утверждение, вы можете добавить LIMIT 1 в подзапрос, чтобы гарантировать, что возвращается не более 1 строки. Это устранит ошибку, ваш код по-прежнему будет бессмыслицей.

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

Практически вы хотите как-то сопоставить строки, а не выбирать произвольную строку из store удаленных таблиц, чтобы обновлять каждую строку вашего локального customer таблицы.
В вашем рудиментарном вопросе не содержится достаточно подробностей, поэтому я предполагаю, что для этого примера текстовый столбец match_name в обеих таблицах (и UNIQUE в store):

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

Но это чрезвычайно дорогой способ делать вещи.

В идеале вы должны полностью переписать выражение.

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;

Это устраняет ряд проблем в вашем первоначальном заявлении.

  • Очевидно, что основная проблема, связанная с вашей ошибкой, исправлена.

  • Почти всегда лучше присоединяться к дополнительным отношениям в предложении FROM оператора UPDATE чем для запуска коррелированных подзапросов для каждой отдельной строки.

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

  • При коррелированных подзапросах, если ни одна строка не найдена в подзапросе, столбец обновляется до NULL, что почти всегда не то, что вы хотите.
    В моей обновленной форме строка обновляется только в том случае, если найдена соответствующая строка. Else, строка не трогается.

  • Обычно вы не хотите обновлять строки, когда ничего не меняется. Это дорого ничего не делает (но все же создает мертвые строки). Последнее выражение в WHERE предотвращает такие пустые обновления:

     AND   c.customer_id IS DISTINCT FROM sub.store_key
    

Ответ 2

Это означает, что ваш вложенный SELECT возвращает более одной строки.

Вам нужно добавить к нему правильное предложение WHERE.

Ответ 3

Эта ошибка означает, что запрос SELECT store_key FROM store возвратил две или более строк в SERVER1 данных SERVER1. Если вы хотите обновить всех клиентов, используйте соединение вместо оператора scalar =. Для этого вам необходимо условие "подключить" клиентов для хранения товаров.

Если вы хотите, чтобы обновить все customer_id к тому же store_key, вам нужно поставить WHERE условие для удаленного исполняемого SELECT, так что запрос возвращает одну строку.

Ответ 4

Результат, созданный Query, не содержит строк, требующих правильной обработки этой проблемы, может быть разрешен, если вы предоставите действительный обработчик в запросе, например: 1. ограничение запроса на возврат одной строки 2. это также можно сделать, выберите max (column) ", который вернет одну строку

Ответ 5

Фундаментальная проблема часто может быть просто решена путем изменения an = to IN, в случаях, когда у вас есть отношения "один ко многим". Например, если вы хотите обновить или удалить кучу учетных записей для данного клиента:

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 there 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 
);