UPSERT con ON CONFLICT utilizando valores de la tabla fuente en la parte ACTUALIZAR


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;

provoca el siguiente error:

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.

¿Cómo hacer la actualización haciendo referencia al contenido de table_a?


55
CREATE TABLE A...crea tabla a, no table_a.
Abelisto

el do update set b = a;no puede encontrar "a" porque hay referencia a la Tabla "b" y no a la Subconsulta, intentedo update set b = (select a from a);
Patrick7

Respuestas:


24

Múltiples problemas
Su configuración, extendida:

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

Esto 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

Los problemas

  1. Eres confuso table_ay Aestás en tu demo (como comentó @Abelisto ).

    El uso de identificadores legales, en minúsculas y sin comillas, ayuda a evitar confusiones.

  2. Como mencionó @Ziggy , ON CONFLICTsolo funciona para infracciones de restricción de exclusión o exclusivas reales . El manual:

    La ON CONFLICTcláusula opcional especifica una acción alternativa para generar una violación única o un error de violación de restricción de exclusión.

    En consecuencia, ON CONFLICT (b)no puede funcionar, no hay restricción allí. ON CONFLICT (pk_b)trabajos.

  3. Como también mencionó @Ziggy , los nombres de las tablas de origen no son visibles en la UPDATEparte. El manual:

    Las cláusulas SETy tienen acceso a la fila existente utilizando el nombre de la tabla (o un alias), y a las filas propuestas para la inserción utilizando la tabla especial .WHEREON CONFLICT DO UPDATEexcluded

    El énfasis audaz es mío.

  4. Tampoco puede usar nombres de columna de la tabla fuente en la UPDATEparte. Deben ser nombres de columna de la fila de destino . Entonces realmente quieres:

    SET    b = excluded.b

    El manual una vez más:

    Tenga en cuenta que los efectos de todos los BEFORE INSERTdesencadenantes por fila se reflejan en valores excluidos, ya que esos efectos pueden haber contribuido a que la fila se excluya de la inserción.


gracias por esta explicación, ahora sé por qué b = excluded.ano puede funcionar, estaba un poco oculto en la Docu oficial.
Patrick7

7

Al realizar upserts en PostgreSQL 9.5+, debe hacer referencia a los datos excluidos (los que no pudieron insertarse) por el alias excluded. Además, la on conflictopción debe referirse a la clave: en (pk_b)lugar de (b). P.ej.

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 obtener más información, consulte la documentación oficial o esta fácil introducción a upsert .


Esta consulta no funciona.
shx
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.