UPSERT com ON CONFLICT usando valores da tabela de origem na parte UPDATE

17

Dado:

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

Esta consulta:

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

causa o seguinte erro:

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.

Como fazer a atualização enquanto se refere ao conteúdo de table_a ?

Tony Indrali
fonte
5
CREATE TABLE A...cria tabela a, não table_a.
Abelisto
o do update set b = a;não consegue encontrar "a", porque há referência à Tabela "b" e não ao Subquery, tentedo update set b = (select a from a);
Patrick7

Respostas:

24

Problemas múltiplos.
Sua configuração, estendida:

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

Isso funciona:

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

Resultado:

TABLE b;

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

Os problemas

  1. Você está confuso table_ae Aem sua demonstração (como o @Abelisto comentou ).

    O uso de identificadores legais, em minúsculas e sem aspas ajuda a evitar confusões.

  2. Como o @Ziggy mencionado , ON CONFLICTfunciona apenas para violações reais de restrição única ou de exclusão . O manual:

    A ON CONFLICTcláusula opcional especifica uma ação alternativa para gerar um erro exclusivo de violação de violação ou restrição de exclusão.

    Conseqüentemente, ON CONFLICT (b)não pode funcionar, sem restrições por lá. ON CONFLICT (pk_b)trabalho.

  3. Como o @Ziggy também mencionado , os nomes das tabelas de origem não são visíveis na UPDATEpeça. O manual:

    As cláusulas SETe têm acesso à linha existente usando o nome da tabela (ou um alias) e às linhas propostas para inserção usando a tabela especial .WHEREON CONFLICT DO UPDATEexcluded

    Negrito ênfase minha.

  4. Você também não pode usar nomes de colunas da tabela de origem na UPDATEpeça. Deve ser o nome da coluna da linha de destino . Então você realmente quer:

    SET    b = excluded.b

    O manual mais uma vez:

    Observe que os efeitos de todos os BEFORE INSERTgatilhos por linha são refletidos em valores excluídos, pois esses efeitos podem ter contribuído para a linha ser excluída da inserção.

Erwin Brandstetter
fonte
obrigado por esta explicação, agora eu sei porque o b = excluded.anão pode funcionar, estava um pouco escondido no Docu oficial.
precisa saber é o seguinte
7

Ao fazer upserts no PostgreSQL 9.5+, você deve se referir aos dados excluídos (aqueles que não foram inseridos) pelo alias excluded. Além disso, a on conflictopção deve se referir à chave: em (pk_b)vez de (b). Por exemplo.

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

Para mais informações, consulte a documentação oficial ou esta introdução fácil ao upsert .

Ziggy Crueltyfree Zeitgeister
fonte
Esta consulta não funciona.
Shx 06/04