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)
Postgres 9.6目前没有直接的方法,因为:
在 中UPDATE
,只有特殊EXCLUDED
行是可见的(除了更新的行)。没有FROM
允许额外的表加入条款。
该EXCLUDED
行正是被冲突拒绝的将要插入的行的状态。正是要插入的表的列。而已。
因此,有没有空间在未提及任何其他列INSERT
在UPDATE
了UPSERT的分支。未来版本可能允许更多。已经讨论了各种添加,但尚未实施。这是一个复杂的问题。
我建议使用 CTE 来解决这个问题。
在独立VALUES
表达式中提供包含您需要的所有列的行。(data
以下示例中的CTE 。)
运行INSERT ... ON CONFLICT ... DO UPDATE
,但不会实际更新任何行(WHERE FALSE
)。所需的副作用:排除的行无论如何都会被锁定,以确保并发使用是安全的。
使用 just ON CONFLICT ... DO NOTHING
,冲突行不会被锁定,并且对于并发事务是公平的游戏。如果不能同时写入同一表的相同行,请使用这种更便宜的变体。
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)
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"
有关的:
归档时间: |
|
查看次数: |
5697 次 |
最近记录: |