it-swarm.com.de

Postgres-Fehler: Mehr als eine Zeile, die von einer als Ausdruck verwendeten Unterabfrage zurückgegeben wird

Ich habe zwei separate Datenbanken. Ich versuche, eine Spalte in einer Datenbank auf die Werte einer Spalte aus der anderen Datenbank zu aktualisieren:

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

Dies ist der Fehler, den ich erhalte:

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

Irgendwelche Ideen?

27
user3182502

Technisch, um Ihre Anweisung zu reparieren, können Sie LIMIT 1 zur Unterabfrage hinzufügen, um sicherzustellen, dass höchstens 1 Zeile vorhanden ist ist zurückgekommen. Das würde den Fehler beseitigen, dein Code wäre trotzdem Unsinn.

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

Praktisch, Sie möchten Zeilen zuordnen irgendwie, anstatt eine beliebige Zeile aus der entfernten Tabelle store zu wählen Aktualisieren Sie jede Zeile Ihrer lokalen Tabelle customer.
Ihre rudimentäre Frage enthält nicht genügend Details, daher bin ich vorausgesetzt eine Textspalte match_name In beiden Tabellen (und UNIQUE in store) für dieses Beispiel:

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

Aber das ist eine extrem teure Art, Dinge zu tun.

Idealerweise, Sie sollten die Anweisung vollständig umschreiben.

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;

Dies behebt eine Reihe von Problemen in Ihrer ursprünglichen Aussage.

  • Offensichtlich ist das Grundproblem, das zu Ihrem Fehler führt, behoben.

  • Es ist fast immer besser, zusätzliche Relationen in der FROM -Klausel einer UPDATE -Anweisung zu verknüpfen, als korrelierte Unterabfragen auszuführen = für jede einzelne Zeile.

  • Bei Verwendung von dblink wird das oben Genannte tausendmal wichtiger. Sie möchten nicht für jede einzelne Zeile dblink() aufrufen, das ist extrem teuer. Rufen Sie es einmal auf, um alle benötigten Zeilen abzurufen.

  • Wenn bei korrelierten Unterabfragen keine Zeile gefunden in der Unterabfrage angegeben ist, wird die Spalte auf NULL aktualisiert, was fast immer nicht Ihren Wünschen entspricht.
    In meinem aktualisierten Formular wird die Zeile nur aktualisiert, wenn eine übereinstimmende Zeile gefunden wird. Ansonsten wird die Reihe nicht berührt.

  • Normalerweise möchten Sie keine Zeilen aktualisieren, wenn sich tatsächlich nichts ändert. Das ist teuer nichts zu tun (aber produziert immer noch tote Reihen). Der letzte Ausdruck in der WHERE -Klausel verhindert solche leere Aktualisierungen:

     AND   c.customer_id IS DISTINCT FROM sub.store_key
    
44

Dies bedeutet, dass Ihr verschachteltes SELECT mehr als eine Zeile zurückgibt.

Sie müssen eine richtige WHERE-Klausel hinzufügen.

3
peter.petrov

Das grundlegende Problem kann oft einfach gelöst werden, indem ein = In IN geändert wird, wenn Sie eine Eins-zu-Viele-Beziehung haben. Wenn Sie beispielsweise eine Reihe von Konten für einen bestimmten Kunden aktualisieren oder löschen möchten:

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

Dieser Fehler bedeutet, dass die Abfrage SELECT store_key FROM store Zwei oder mehr Zeilen in der Datenbank SERVER1 Zurückgegeben hat. Wenn Sie alle Kunden aktualisieren möchten, verwenden Sie einen Join anstelle eines skalaren Operators =. Sie benötigen eine Bedingung, um Kunden mit dem Speichern von Artikeln zu "verbinden", um dies zu tun.

Wenn Sie alle customer_id Auf denselben store_key Aktualisieren möchten, müssen Sie eine WHERE -Klausel für die remote ausgeführte SELECT angeben, damit die Abfrage zurückgegeben wird eine einzelne Reihe.

1
dasblinkenlight

Das Ergebnis der Abfrage besteht darin, dass keine der Zeilen ordnungsgemäß behandelt werden müssen. Dieses Problem kann behoben werden, wenn Sie den gültigen Handler in der Abfrage angeben, z. B. 1. Beschränken der Abfrage auf die Rückgabe einer einzelnen Zeile. Wählen Sie "max (column)", um die einzelne Zeile zurückzugeben

0
user10101