UPSERT dengan ON CONFLICT menggunakan nilai-nilai dari tabel sumber di bagian UPDATE

17

Diberikan:

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

Kueri ini:

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

menyebabkan kesalahan berikut:

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.

Bagaimana melakukan pembaruan sambil merujuk ke konten table_a?

Tony Indrali
sumber
5
CREATE TABLE A...membuat tabel a, bukan table_a.
Abelisto
yang do update set b = a;tidak dapat menemukan "a" karena ada referensi Table "b" dan tidak ke Subquery, cobalahdo update set b = (select a from a);
Patrick7

Jawaban:

24

Berbagai masalah.
Pengaturan Anda, diperluas:

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

Ini bekerja:

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

Hasil:

TABLE b;

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

Masalah

  1. Anda membingungkan table_adan Adalam demo Anda (seperti komentar @Abelisto ).

    Menggunakan pengidentifikasi legal, huruf kecil, tanda kutip membantu menghindari kebingungan.

  2. Seperti @Ziggy yang disebutkan , ON CONFLICThanya berfungsi untuk pelanggaran batasan unik atau pengecualian . Manual:

    ON CONFLICTKlausa opsional menentukan tindakan alternatif untuk meningkatkan kesalahan unik pelanggaran atau pengecualian pelanggaran batasan.

    Akibatnya, ON CONFLICT (b)tidak bisa bekerja, tidak ada kendala di sana. ON CONFLICT (pk_b)bekerja.

  3. Seperti @Ziggy juga disebutkan , nama tabel sumber tidak terlihat di UPDATEbagian itu. Manual:

    The SETdan WHEREklausa dalam ON CONFLICT DO UPDATEmemiliki akses ke baris yang sudah ada menggunakan nama tabel (atau alias), dan untuk baris diusulkan untuk penyisipan menggunakan khusus excludedtabel .

    Penekanan berani saya.

  4. Anda juga tidak bisa menggunakan nama kolom dari tabel sumber di UPDATEbagian tersebut. Itu harus nama kolom dari baris target . Jadi, Anda benar-benar ingin:

    SET    b = excluded.b

    Manual sekali lagi:

    Perhatikan bahwa efek dari semua BEFORE INSERTpemicu setiap baris dicerminkan dalam nilai yang dikecualikan, karena efek tersebut mungkin berkontribusi pada baris yang dikecualikan dari penyisipan.

Erwin Brandstetter
sumber
terima kasih atas penjelasan ini, sekarang saya tahu mengapa b = excluded.atidak bisa bekerja, itu sedikit tersembunyi di Docu resmi.
Patrick7
7

Saat melakukan upserts di PostgreSQL 9.5+ Anda harus merujuk ke data yang dikecualikan (yang gagal disisipkan) oleh alias excluded. Selain itu, on conflictopsi harus merujuk pada kunci: (pk_b)daripada (b). Misalnya.

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

Untuk informasi lebih lanjut lihat dokumentasi resmi atau pengantar yang mudah ini .

Ziggy Crueltyfree Zeitgeister
sumber
Kueri ini tidak berfungsi.
shx