web-dev-qa-db-ja.com

列ではないON CONFLICT句に値を渡す方法は?

基本的に、私はpostgresqlの単一のコマンドで複数の挿入を行おうとしていますが、私の_ON CONFLICT_には挿入ごとのパラメーターがあります。

以下はクエリです(_psycopg2_の形式)。

_INSERT INTO
    web_pages
    (url, starturl, netloc, distance, is_text, 
     priority, type, addtime, state)
VALUES
    (%(url)s, %(starturl)s, %(netloc)s, %(distance)s, %(is_text)s, 
      %(priority)s, %(type)s, %(addtime)s, %(state)s)
ON CONFLICT (url) DO
    UPDATE
        SET
            state           = EXCLUDED.state,
            starturl        = EXCLUDED.starturl,
            netloc          = EXCLUDED.netloc,
            is_text         = EXCLUDED.is_text,
            distance        = LEAST(EXCLUDED.distance, web_pages.distance),
            priority        = GREATEST(EXCLUDED.priority, web_pages.priority),
            addtime         = LEAST(EXCLUDED.addtime, web_pages.addtime)
        WHERE
        (
                web_pages.ignoreuntiltime < %(ignoreuntiltime)s
            AND
                web_pages.url = EXCLUDED.url
            AND
                (web_pages.state = 'complete' OR web_pages.state = 'error')
        )
    ;
_

複数のVALUESタプルを渡せるようにしたいのですが、%(ignoreuntiltime)sは値のタプルごとに異なる場合があります。

各行のvaluesタプルなどの追加パラメーターとして指定できる方法はありますか?

私が欲しいのは一種です:

_INSERT INTO
    web_pages
    (url, starturl, netloc, distance, is_text, 
     priority, type, addtime, state, not_a_column)
VALUES
    (%(url)s, %(starturl)s, %(netloc)s, %(distance)s, %(is_text)s, 
     %(priority)s, %(type)s, %(addtime)s, %(state)s, %(ignoreuntiltime)s)
    ....(more value tuples here)....
ON CONFLICT (url) DO
    UPDATE
        SET
            state           = EXCLUDED.state,
            starturl        = EXCLUDED.starturl,
            netloc          = EXCLUDED.netloc,
            is_text         = EXCLUDED.is_text,
            distance        = LEAST(EXCLUDED.distance, web_pages.distance),
            priority        = GREATEST(EXCLUDED.priority, web_pages.priority),
            addtime         = LEAST(EXCLUDED.addtime, web_pages.addtime)
        WHERE
        (
                web_pages.ignoreuntiltime < EXCLUDED.not_a_column
            AND
                web_pages.url = EXCLUDED.url
            AND
                (web_pages.state = 'complete' OR web_pages.state = 'error')
        )
    ;
_
6
Fake Name

不可能です。

現在、Postgres9.6には直接的な方法はありません。

  • UPDATEでは、(更新された行に加えて)特別なEXCLUDED行のみが表示されます。追加のテーブルに結合することを許可されているFROM句はありません。

  • EXCLUDED行は、競合により拒否された挿入予定の行の状態です。正確に挿入されるテーブルの列。これ以上何もない。

したがって、UPSERTのINSERTブランチのUPDATEに記載されていない追加の列を配置する余地はありません。将来のバージョンではさらに多くのものが可能になるかもしれません。さまざまな追加について説明されていますが、まだ実装されていません。 複雑な問題です。

クリーンな回避策

CTEを使用してこの回避策を提案します。

  1. 独立したVALUES式で必要なすべての列を含む行を提供します。 (以下の例ではCTE data)。

  2. INSERT ... ON CONFLICT ... DO UPDATEを実行しますが、実際には行(WHERE FALSE)を更新しないでください。望ましい副作用:除外された行は、を同時に使用しても安全になるようにロックされます
    ON CONFLICT ... DO NOTHINGだけでは、競合する行はロックされず、同時トランザクションの公平なゲームになります。 If同じテーブルの同じ行への同時書き込みはできないため、この安価なバリアントを使用します。

  3. 同じコマンドの一部として別のUPDATEを実行します。 CTE dataからのすべての列-必要に応じて、任意の追加テーブルにも結合します。

デモ表:

CREATE TEMP TABLE tbl (tbl_id int PRIMARY KEY, note text);
INSERT INTO tbl VALUES
   (1, 'old1')
 , (2, 'old2');

デモクエリ:

WITH data (tbl_id, note) AS (
   VALUES
      (int '1', text 'update')   -- explicit type declarations for free standing VALUES
    , (2, 'no update')           -- going to exclude row in WHERE of UPDATE
    , (3, 'insert')              -- not going to INSERT the "note"
   )
, ins AS (
   INSERT INTO tbl AS t (tbl_id) -- "note" not inserted
   SELECT tbl_id FROM data
   ON     CONFLICT (tbl_id) DO UPDATE
   SET    note = NULL
   WHERE  FALSE                  -- never executed, but locks row.
   RETURNING t.tbl_id
   )
UPDATE tbl t
SET    note = d.note             -- now we can!
FROM   data d
LEFT   JOIN ins i USING (tbl_id)
WHERE  i.tbl_id IS NULL
AND    t.tbl_id = d.tbl_id
AND    d.note <> 'no update';    -- just to demonstrate use in WHERE

結果:

TABLE tbl ORDER BY tbl_id;
 tbl_id |  note
--------+--------
      1 | update   -- updated
      2 | old2     -- not updated because of WHERE in UPDATE
      3 |          -- inserted without "note"

関連:

5