postgresql 为什么在带UNION的Postgres UPSERT中得到一个空结果

8yparm6h  于 2022-12-23  发布在  PostgreSQL
关注(0)|答案(1)|浏览(143)

在PG(nodejs)中尝试以下UPSERT:

WITH upsert_result AS(
    INSERT INTO table1 (col1, col2)
    VALUES($1,$2)
    ON CONFLICT DO NOTHING
    RETURNING *
)
SELECT * FROM upsert_result
UNION
SELECT * FROM table1 WHERE col1=$1

为什么我有时会得到空结果?
我的理解是,这里的UNION应该通过获取冲突行来防止这种情况。
以下是表定义:

CREATE EXTENSION IF NOT EXISTS "ltree";

CREATE TABLE IF NOT EXISTS table1(
    col1 TEXT NOT NULL UNIQUE REFERENCES table2 (col1) ON DELETE CASCADE,
    col2 ltree NOT NULL
);
CREATE INDEX IF NOT EXISTS tree_col2_idx ON table1 USING gist (col2);

我期望查询返回新插入的行(如果具有相同col1的行不存在)或现有行(如果具有相同col1的行已经存在)。实际上,这是大多数情况下发生的事情,但在一些罕见的情况下,我从上面的插入中得到一个空结果。

kgsdhlau

kgsdhlau1#

存在隐藏的争用条件
冲突检查包括(必须包括!)来自(尚未)提交的并发事务处理的行。如果一个并发事务处理写入了您的事务处理现在试图UPSERT的行,则您的事务处理必须等待另一个并发事务处理完成。
如果另一个事务正常结束,INSERT将检测到冲突和DO NOTHING,因此也不会返回该行。SELECT看到的是与查询开始时相同的快照,也无法返回尚未看到的行。
在此阅读详细的评估,尤其是“并发问题1”一章:

  • 如何在PostgreSQL中使用RETURNING和ON CONFLICT?

......以及如何克服它。
旁注:UNION ALL,不是UNION,但这也在链接答案中。

相关问题