web-dev-qa-db-ja.com

UPDATE部分のソーステーブルの値を使用するON CONFLICTを指定したUPSERT

与えられた:

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

このクエリ:

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

次のエラーが発生します。

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.

table_aの内容を参照しながら更新する方法は?

20
Tony Indrali

複数の問題。
拡張されたセットアップ:

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

これは機能します:

_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
_

結果:

_TABLE b;

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

問題点

  1. デモで_table_a_とAを混乱させています( @ Abelistoがコメントした など)。

    正当な小文字の引用符で囲まれていない識別子を使用すると、混乱を避けることができます。

  2. @ Ziggyについて と同様に、_ON CONFLICT_は実際の一意または除外制約違反に対してのみ機能します。 マニュアル:

    オプションの_ON CONFLICT_句は、一意の違反または除外制約違反エラーを発生させる代替アクションを指定します。

    したがって、 ON CONFLICT (b) 機能せず、そこに制約はありません。 ON CONFLICT (pk_b)は機能します。

  3. @ Ziggyについても言及 と同様に、UPDATE部分のsourceテーブル名は表示されませんマニュアル:

    _ON CONFLICT DO UPDATE_のSET句とWHERE句は、テーブルの名前(またはエイリアス)を使用して既存の行にアクセスし、特殊なexcluded table

    大胆な強調鉱山。

  4. また、UPDATE部分でsourceテーブルの列名を使用することはできません。 target行の列名でなければなりません。だからあなたは本当に欲しい:

    _SET    b = excluded.b
    _

    マニュアルをもう一度:

    行ごとの_BEFORE INSERT_トリガーの影響は、除外された値に反映されることに注意してください。これらの影響は、挿入から除外される行に寄与した可能性があるためです。

28

PostgreSQL 9.5以降でアップサートを実行するときは、除外されたデータ(挿入に失敗したデータ)をエイリアスexcludedで参照する必要があります。また、on conflictオプションはキーを参照する必要があります:(pk_b) のではなく (b)。例えば。

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

詳細については、公式ドキュメントまたは psertの簡単な紹介 を参照してください。