UPSERT avec ON CONFLICT utilisant les valeurs de la table source dans la partie UPDATE


17

Donné:

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

Cette requête:

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

provoque l'erreur suivante:

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.

Comment faire la mise à jour en se référant au contenu de table_a?


5
CREATE TABLE A...crée une table a, non table_a.
Abelisto

le do update set b = a;ne peut pas trouver "a" car il fait référence au tableau "b" et non à la sous-requête, essayezdo update set b = (select a from a);
Patrick7

Réponses:


24

Problèmes multiples.
Votre configuration, étendue:

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

Cela marche:

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

Résultat:

TABLE b;

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

Les problèmes

  1. Vous êtes déroutant table_aet Adans votre démo (comme l' a commenté @Abelisto ).

    L'utilisation d'identificateurs légaux, minuscules et non cotés aide à éviter toute confusion.

  2. Comme @Ziggy l'a mentionné , ON CONFLICTne fonctionne que pour les violations réelles de contraintes uniques ou d'exclusion . Le manuel:

    La ON CONFLICTclause facultative spécifie une action alternative pour déclencher une erreur de violation de violation ou de contrainte d'exclusion unique.

    Par conséquent, ON CONFLICT (b)ne peut pas fonctionner, aucune contrainte là-bas. ON CONFLICT (pk_b)travaux.

  3. Comme @Ziggy l'a également mentionné , les noms des tables source ne sont pas visibles dans la UPDATEpièce. Le manuel:

    Les clauses SETet ont accès à la ligne existante en utilisant le nom de la table (ou un alias), et aux lignes proposées pour l'insertion en utilisant la table spéciale .WHEREON CONFLICT DO UPDATEexcluded

    Accentuation sur moi.

  4. Vous ne pouvez pas non plus utiliser les noms de colonne de la table source dans la UPDATEpièce. Il doit s'agir des noms de colonne de la ligne cible . Vous voulez donc vraiment:

    SET    b = excluded.b

    Le manuel une fois de plus:

    Notez que les effets de tous les BEFORE INSERTdéclencheurs par ligne se reflètent dans les valeurs exclues, car ces effets peuvent avoir contribué à exclure la ligne de l'insertion.


merci pour cette explication, maintenant je sais pourquoi b = excluded.aça ne marche pas, c'était un peu caché dans le Docu officiel.
Patrick7

7

Lorsque vous effectuez des upserts dans PostgreSQL 9.5+, vous devez vous référer aux données exclues (celles qui n'ont pas pu être insérées) par l'alias excluded. De plus, l' on conflictoption doit faire référence à la clé: (pk_b)plutôt qu'à (b). Par exemple.

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

Pour plus d'informations, reportez-vous à la documentation officielle ou à cette introduction facile à upsert .


Cette requête ne fonctionne pas .
shx
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.