如何将值传递给不是列的 ON CONFLICT 子句?

Fak*_*ame 6 postgresql upsert postgresql-9.6

基本上,我试图在 postgresql 中的单个命令中进行多次插入,其中我ON CONFLICT有一个 per-insert 参数。

这是查询(格式为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')
        )
    ;
Run Code Online (Sandbox Code Playgroud)

我希望能够传递多个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')
        )
    ;
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 5

不可能,因为

Postgres 9.6目前没有直接的方法,因为:

  • 在 中UPDATE,只有特殊EXCLUDED行是可见的(除了更新的行)。没有FROM允许额外的表加入条款。

  • EXCLUDED行正是被冲突拒绝的将要插入的行的状态。正是要插入的表的列。而已。

因此,有没有空间在未提及任何其他列INSERTUPDATE了UPSERT的分支。未来版本可能允许更多。已经讨论了各种添加,但尚未实施。这是一个复杂的问题。

干净的解决方法

我建议使用 CTE 来解决这个问题。

  1. 在独立VALUES表达式中提供包含您需要的所有列的行。(data以下示例中的CTE 。)

  2. 运行INSERT ... ON CONFLICT ... DO UPDATE,但不会实际更新任何行(WHERE FALSE)。所需的副作用:排除的行无论如何都会被锁定,以确保并发使用安全的
    使用 just ON CONFLICT ... DO NOTHING,冲突行不会被锁定,并且对于并发事务是公平的游戏。如果不能同时写入同一表的相同行,请使用这种更便宜的变体。

  3. UPDATE作为同一命令的一部分运行一个单独的命令,您可以在其中使用CTE 中的所有data- 并在需要时加入任意附加表。

演示表:

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

演示查询:

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
Run Code Online (Sandbox Code Playgroud)

结果:

TABLE tbl ORDER BY tbl_id;
Run Code Online (Sandbox Code Playgroud)
 tbl_id |  note
--------+--------
      1 | update   -- updated
      2 | old2     -- not updated because of WHERE in UPDATE
      3 |          -- inserted without "note"
Run Code Online (Sandbox Code Playgroud)

有关的: