UPSERT z ON CONFLICT przy użyciu wartości z tabeli źródłowej w części UPDATE

18

Dany:

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

To zapytanie:

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

powoduje następujący błąd:

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.

Jak wykonać aktualizację, odnosząc się do treści table_a?

Tony Indrali
źródło
5
CREATE TABLE A...tworzy tabelę a, a nie table_a.
Abelisto,
do update set b = a;nie może znaleźć „a”, ponieważ istnieje odniesienie do tabeli „B”, a nie do podzapytania, spróbujdo update set b = (select a from a);
Patrick7

Odpowiedzi:

25

Wiele problemów.
Twoja konfiguracja rozszerzona:

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

To działa:

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

Wynik:

TABLE b;

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

Problemy

  1. Jesteś zagubionytable_a i Ana swoim demie (jak komentował @Abelisto ).

    Używanie legalnych, małych i niewymienionych identyfikatorów pomaga uniknąć nieporozumień.

  2. Jak wspomniano w @Ziggy , ON CONFLICTdziała tylko w przypadku rzeczywistych naruszeń ograniczeń unikatowych lub wykluczających . Instrukcja:

    Opcjonalna ON CONFLICTklauzula określa działanie alternatywne w stosunku do zgłaszania unikalnego błędu naruszenia lub ograniczenia naruszenia ograniczenia wykluczenia.

    W związku z tym ON CONFLICT (b)nie może działać, nie ma ograniczeń. ON CONFLICT (pk_b)Pracuje.

  3. Jak wspomniano również @Ziggy , nazwy tabel źródłowych nie są widoczne w UPDATEczęści. Instrukcja:

    SETI WHEREklauzul ON CONFLICT DO UPDATEmieć dostęp do istniejącego rzędu przez nazwę stołu (lub alias) oraz rzędów proponowane do wprowadzania za pomocą specjalnego excludedtabeli .

    Odważny nacisk moje.

  4. W części nie można również używać nazw kolumn tabeli źródłowejUPDATE . Musi to być nazwa kolumny docelowego wiersza . Więc naprawdę chcesz:

    SET    b = excluded.b

    Instrukcja jeszcze raz:

    Należy pamiętać, że efekty wszystkich BEFORE INSERTwyzwalaczy w wierszu są odzwierciedlone w wartościach wykluczonych, ponieważ te efekty mogły przyczynić się do wykluczenia wiersza z wstawiania.

Erwin Brandstetter
źródło
dzięki za wyjaśnienie, teraz wiem, dlaczego to b = excluded.anie działa, było to trochę ukryte w oficjalnym Docu.
Patrick7
prosta jedna linijka dla manekinów, takich jak: „wykluczone” wskazuje na nowe przychodzące dane, które chcesz wstawić lub pozostaną w tabeli.
user92674,
8

Podczas wykonywania aktualizacji w PostgreSQL 9.5+ musisz odwoływać się do wykluczonych danych (tych, których nie udało się wstawić) przez alias excluded. Ponadto on conflictopcja musi odnosić się do klucza: (pk_b)zamiast (b). Na przykład.

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

Aby uzyskać więcej informacji, zapoznaj się z oficjalną dokumentacją lub z tym łatwym wprowadzeniem do upsert .

Ziggy Crueltyfree Zeitgeister
źródło
To zapytanie nie działa.
shx