UPSERT con ON CONFLICT utilizzando i valori della tabella di origine nella parte UPDATE


17

Dato:

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

Questa query:

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

provoca il seguente errore:

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.

Come eseguire l'aggiornamento facendo riferimento al contenuto di table_a?


5
CREATE TABLE A...crea tabella a, no table_a.
Abelisto,

il do update set b = a;non riesce a trovare "a" perché non c'è riferimento alla Tabella "B" e non alla sottoquery, provaredo update set b = (select a from a);
Patrick7

Risposte:


24

Problemi multipli.
La tua configurazione, estesa:

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

Questo funziona:

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

Risultato:

TABLE b;

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

I problemi

  1. Sei confuso table_ae Anella tua demo (come commentato da @Abelisto ).

    L'uso di identificatori legali, minuscoli e non quotati aiuta a evitare confusione.

  2. Come menzionato da @Ziggy , ON CONFLICTfunziona solo per le effettive violazioni di vincoli univoci o di esclusione . Il manuale:

    La ON CONFLICTclausola facoltativa specifica un'azione alternativa alla generazione di una violazione unica o di un errore di violazione del vincolo di esclusione.

    Di conseguenza, ON CONFLICT (b)non può funzionare, nessun vincolo lì. ON CONFLICT (pk_b)lavori.

  3. Come menzionato anche @Ziggy , i nomi delle tabelle di origine non sono visibili nella UPDATEparte. Il manuale:

    Le clausole SETe hanno accesso alla riga esistente utilizzando il nome della tabella (o un alias) e alle righe proposte per l'inserimento utilizzando la tabella speciale .WHEREON CONFLICT DO UPDATEexcluded

    Enorme enfasi sulla mia.

  4. Inoltre, non è possibile utilizzare i nomi di colonna della tabella di origine nella UPDATEparte. Devono essere i nomi di colonna della riga di destinazione . Quindi vuoi davvero:

    SET    b = excluded.b

    Il manuale ancora una volta:

    Si noti che gli effetti di tutti i BEFORE INSERTtrigger per riga si riflettono nei valori esclusi, poiché tali effetti potrebbero aver contribuito a escludere la riga dall'inserimento.


grazie per questa spiegazione, ora so perché b = excluded.anon può funzionare, è stato un po 'nascosto nel Docu ufficiale.
Patrick7,

7

Quando si eseguono aggiornamenti in PostgreSQL 9.5+, è necessario fare riferimento ai dati esclusi (ciò che non è stato possibile inserire) dall'alias excluded. Inoltre, l' on conflictopzione deve fare riferimento alla chiave: (pk_b)anziché (b). Per esempio.

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

Per ulteriori informazioni, consultare la documentazione ufficiale o questa semplice introduzione a upsert .


Questa query non funziona.
shx,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.