我有一个upsert语句(http://www.the-art-of-web.com/sql/upsert/)只要一个id不存在的行进行插入,并在该行存在时更新列:
WITH upsert AS
(UPDATE foo SET counter=counter+1 WHERE id='bar' RETURNING *)
INSERT INTO foo(id, counter) SELECT 'bar', 0
WHERE NOT EXISTS (SELECT * FROM upsert) RETURNING counter;
id是主键列(如预期的那样)。直到这里一切正常。
但是有第3列“位置”可用于自定义排序。 如果有更新,我想保留当前值。
但是insert语句需要一个额外的子查询返回未使用的最低位置:
WITH upsert AS
(UPDATE foo SET counter=counter+1 WHERE id='bar' RETURNING *)
INSERT INTO foo(id, counter, position) SELECT 'bar', 0, MIN( position)-1 from foo
WHERE NOT EXISTS (SELECT * FROM upsert) RETURNING counter;
使用此语句我收到错误
ERROR: duplicate key value violates unique constraint "id"
这里有什么不对吗?
答案 0 :(得分:2)
问题是应用于0行的MIN()
返回一行(具有NULL值)
示例:
test=> select min(1) where false;
min
-----
(1 row)
这与没有WHERE
min()
子句不同
test=> select 1 where false;
?column?
----------
(0 rows)
因此,当在提供MIN()
的子查询中使用INSERT
时,即使WHERE
子句的计算结果为false,它也会插入一个新行,这会破坏此UPSERT的逻辑。 / p>
我认为这可以通过引入另一个子查询来解决:
WITH upsert AS
(UPDATE foo SET counter=counter+1 WHERE id='bar' RETURNING *)
INSERT INTO foo(id, counter, position)
SELECT * FROM (SELECT 'bar', 0, MIN( position)-1 from foo) s
WHERE NOT EXISTS (SELECT * FROM upsert)
RETURNING counter;
但请注意,将其塞入单个SQL语句并不能保证在并发运行时系统成功。
了解更多:
How do I do an UPSERT (MERGE, INSERT … ON DUPLICATE UPDATE) in PostgreSQL?