it-swarm.com.de

UPSERT mit ON CONFLICT unter Verwendung von Werten aus der Quelltabelle im UPDATE-Teil

Gegeben:

CREATE TABLE A (
PK_A INT8 NOT NULL,
A INT8,
PRIMARY KEY (PK_A)
);

CREATE TABLE B (
PK_B INT8 NOT NULL,
B INT8,
PRIMARY KEY (PK_B)
);

Diese Abfrage:

insert into table_b (pk_b, b) 
select pk_a,a from table_a 
on conflict (b) do update set b=a;

verursacht folgenden Fehler:

ERROR:  column "a" does not exist
LINE 1: ...elect pk_a,a from table_a on conflict (b) do update set b=a;
                                                                 ^
HINT:  There is a column named "a" in table "*SELECT*", but it cannot be referenced from this part of the query.

Wie wird das Update unter Bezugnahme auf den Inhalt von table_a Durchgeführt?

20
Tony Indrali

Mehrere Probleme.
Ihr Setup, erweitert:

CREATE TABLE a (
  pk_a int PRIMARY KEY 
, a int
, comment text  -- added column to make effect clear
);

CREATE TABLE b (
  pk_b int PRIMARY KEY
, b int 
, comment text
);

INSERT INTO a VALUES (1, 11, 'comment from a')
                   , (2, 22, 'comment from a');

INSERT INTO b VALUES (1, 77, 'comment from b');

Das funktioniert:

INSERT INTO b (pk_b, b, comment) 
SELECT pk_a, a, comment
FROM   a 
ON     CONFLICT (pk_b) DO UPDATE  -- conflict is on the unique column
SET    b = excluded.b;            -- key Word "excluded", refer to target column

Ergebnis:

TABLE b;

 pk_b | b  |    comment
------+----+----------------
    1 | 11 | comment from b   -- updated
    2 | 22 | comment from a   -- inserted

Die Probleme

  1. Sie verwechseln table_a Und A in Ihrer Demo (wie @ Abelisto kommentiert ).

    Die Verwendung von legalen, nicht in Anführungszeichen gesetzten Bezeichnern in Kleinbuchstaben hilft, Verwirrung zu vermeiden.

  2. Wie @ Ziggy erwähnt funktioniert ON CONFLICT Nur für tatsächliche eindeutige oder Ausschlussbeschränkungsverletzungen . Das Handbuch :

    Die optionale Klausel ON CONFLICT Gibt eine alternative Aktion zum Auslösen eines eindeutigen Verstoß- oder Ausschlussbeschränkungsfehlers an.

    Folglich, ON CONFLICT (b) kann nicht funktionieren, keine Einschränkung dort. ON CONFLICT (pk_b) funktioniert.

  3. Wie @ Ziggy auch erwähnt , Quelle Tabellennamen sind im Teil UPDATE nicht sichtbar . Das Handbuch :

    Die Klauseln SET und WHERE in ON CONFLICT DO UPDATE Haben Zugriff auf die vorhandene Zeile unter Verwendung des Tabellennamens (oder eines Alias) und auf Zeilen, für die vorgeschlagen wurde Einfügen mit der speziellen Tabelle excluded.

    Meine kühne Betonung.

  4. Sie können auch keine Spaltennamen der Tabelle source Im Teil UPDATE verwenden. Es müssen Spaltennamen der Zeile target sein . Also willst du wirklich:

    SET    b = excluded.b
    

    Das Handbuch noch einmal :

    Beachten Sie, dass sich die Auswirkungen aller zeilenweisen BEFORE INSERT - Trigger in ausgeschlossenen Werten widerspiegeln, da diese Auswirkungen möglicherweise dazu beigetragen haben, dass die Zeile vom Einfügen ausgeschlossen wurde.

28

Wenn Sie Upsets in PostgreSQL 9.5+ ausführen, müssen Sie auf die ausgeschlossenen Daten (die nicht eingefügt werden konnten) durch den Alias ​​excluded verweisen. Auch die on conflict Option muss sich auf den Schlüssel beziehen: (pk_b) eher, als (b). Z.B.

insert into table_b (pk_b, b) 
select pk_a,a from table_a 
on conflict (pk_b) do update set b=excluded.b;

Weitere Informationen finden Sie in der offiziellen Dokumentation oder diese einfache Einführung in upsert .