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

17

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 den 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 mache ich das Update unter Bezugnahme auf den Inhalt von table_a?

Tony Indrali
quelle
5
CREATE TABLE A...Tabelle erstellt a, nicht table_a.
Abelisto
die do update set b = a;nicht finden kann „a“ , weil es Bezugnahme auf die Tabelle „b“ und nicht auf die Subquery, versuchendo update set b = (select a from a);
Patrick7

Antworten:

24

Vielfache Probleme.
Dein 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 sind verwirrend table_aund Ain Ihrer Demo (wie @Abelisto kommentiert ).

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

  2. Wie @Ziggy bereits erwähnt , ON CONFLICTfunktioniert dies nur bei tatsächlichen Verstößen gegen eindeutige oder Ausschlussbedingungen . Das Handbuch:

    Die optionale ON CONFLICTKlausel gibt eine alternative Aktion zum Auslösen eines Fehlers bei einem Verstoß gegen eine eindeutige Verletzung oder Ausschlussbedingung an.

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

  3. Wie @Ziggy erwähnte auch , Quellentabellennamen nicht sichtbar sind im UPDATETeil. Das Handbuch:

    Die Klauseln SETund WHEREin ON CONFLICT DO UPDATEhaben Zugriff auf die vorhandene Zeile unter Verwendung des Tabellennamens (oder eines Alias) und auf Zeilen, die unter Verwendung der speziellen excludedTabelle zum Einfügen vorgeschlagen werden .

    Meine kühne Betonung.

  4. Sie können im Teil auch keine Spaltennamen der Quellentabelle verwendenUPDATE . Es muss Spaltennamen der Zielreihe . Sie wollen also wirklich:

    SET    b = excluded.b

    Das Handbuch noch einmal:

    Beachten Sie, dass die Auswirkungen aller BEFORE INSERTAuslöser pro Zeile in ausgeschlossenen Werten widergespiegelt werden, da diese Auswirkungen möglicherweise dazu beigetragen haben, dass die Zeile vom Einfügen ausgeschlossen wird.

Erwin Brandstetter
quelle
danke für diese erklärung, jetzt weiß ich, warum das b = excluded.anicht funktioniert, es war ein bisschen im offiziellen Docu versteckt.
Patrick7
7

Wenn Sie Upserts in PostgreSQL 9.5+ ausführen, müssen Sie sich auf die ausgeschlossenen Daten (die, die nicht eingefügt werden konnten) des Alias ​​beziehen excluded. Die on conflictOption muss sich auch auf den Schlüssel beziehen: (pk_b)anstatt (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 in dieser einfachen Einführung zu upsert .

Ziggy Crueltyfree Zeitgeister
quelle
Diese Abfrage funktioniert nicht .
shx