web-dev-qa-db-fra.com

Erreur Postgres: plusieurs lignes renvoyées par une sous-requête utilisée comme expression

J'ai deux bases de données distinctes. J'essaie de mettre à jour une colonne d'une base de données avec les valeurs d'une colonne de l'autre base de données:

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

C'est l'erreur que je reçois:

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

Des idées?

27
user3182502

Techniquement, pour réparer votre relevé, vous pouvez ajouter LIMIT 1 à la sous-requête pour garantir qu’au plus 1 ligne est renvoyée. Cela éliminerait l'erreur, votre code serait toujours un non-sens.

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

pratiquement, vous souhaitez faire correspondre les lignes d'une certaine manière au lieu de choisir une ligne arbitraire depuis la table distante store pour mettre à jour chaque ligne de votre table locale customer.
Votre question rudimentaire ne fournit pas assez de détails, alors je suis en supposant une colonne de texte match_name Dans les deux tables (et UNIQUE dans store) pour cet exemple:

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

Mais c'est une façon extrêmement coûteuse de faire les choses.

Idéalement, vous devriez réécrire complètement la déclaration .

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;

Cela résout un certain nombre de problèmes dans votre déclaration initiale.

  • Évidemment, le problème de base qui a entraîné votre erreur est corrigé.

  • Il est presque toujours préférable de joindre des relations supplémentaires dans la FROM clause d’une instruction UPDATE plutôt que d’exécuter des sous-requêtes corrélées pour chaque ligne individuelle.

  • Lorsque vous utilisez dblink, ce qui précède devient mille fois plus important. Vous ne voulez pas appeler dblink() pour chaque ligne, c'est extrêmement coûteux . Appelez-le une fois pour récupérer toutes les lignes dont vous avez besoin.

  • Avec les sous-requêtes corrélées, si aucune ligne n'est trouvée dans la sous-requête, la colonne est mise à jour en NULL, ce qui n'est presque toujours pas ce que vous voulez.
    Dans mon formulaire mis à jour, la ligne n'est mise à jour que si une ligne correspondante est trouvée. Sinon, la ligne n'est pas touchée.

  • Normalement, vous ne voudriez pas mettre à jour les lignes, quand rien ne change réellement. Cela ne coûte rien (mais produit toujours des lignes mortes). La dernière expression de la clause WHERE empêche de telles mises à jour vides :

     AND   c.customer_id IS DISTINCT FROM sub.store_key
    
44
Erwin Brandstetter

Cela signifie que votre SELECT imbriqué retourne plus d'une ligne.

Vous devez ajouter une clause WHERE appropriée.

3
peter.petrov

Le problème fondamental peut souvent être simplement résolu en changeant un = à IN, dans les cas où vous avez une relation un à plusieurs. Par exemple, si vous souhaitez mettre à jour ou supprimer un groupe de comptes pour un client donné:

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's 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 
);
1
rotarydial

Cette erreur signifie que le SELECT store_key FROM store La requête a renvoyé deux lignes ou plus dans la SERVER1 base de données. Si vous souhaitez mettre à jour tous les clients, utilisez une jointure au lieu d'un scalaire = opérateur. Vous avez besoin d'une condition pour "connecter" les clients afin de stocker des éléments afin de le faire.

Si vous souhaitez mettre à jour tous les customer_ids au même store_key, vous devez fournir une clause WHERE à SELECT exécuté à distance afin que la requête renvoie une seule ligne.

1
dasblinkenlight

Le résultat généré par la requête ne contient pas de lignes nécessitant une gestion appropriée. Ce problème peut être résolu si vous indiquez le gestionnaire valide dans la requête, tel que 1. Limiter la requête pour renvoyer une seule ligne 2. Pour cela, vous pouvez également " select max (column) "qui retournera la seule ligne

0
user10101