关于postgresql:为什么这个Postgres查询会抛出“重复键值违反唯一约束”?

Why is this Postgres query throwing “duplicate key value violates unique constraint”?

本问题已经有最佳答案,请猛点这里访问。

我实现了如下简单的更新/插入查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- NOTE: :time is replaced in real code, ids are placed statically for example purposes
-- set status_id=1 to existing rows, update others
UPDATE account_addresses
SET status_id = 1, updated_at = :TIME
WHERE account_id = 1
  AND address_id IN (1,2,3)
  AND status_id IN (2);

-- filter values according to what that update query returns, i.e. construct query like this to insert remaining new records:
INSERT INTO account_addresses (account_id, address_id, status_id, created_at, updated_at)
SELECT account_id, address_id, status_id, created_at::timestamptz, updated_at::timestamptz
FROM (VALUES (1,1,1,:TIME,:TIME),(1,2,1,:TIME,:TIME)) AS sub(account_id, address_id, status_id, created_at, updated_at)
WHERE NOT EXISTS (
  SELECT 1 FROM account_addresses AS aa2
  WHERE aa2.account_id = sub.account_id AND aa2.address_id = sub.address_id
)
RETURNING id;

-- throws:
-- PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint"..."
-- DETAIL:  Key (account_id, address_id)=(1, 1) already exists.

我这样做的原因是:记录可能存在于status_id=2中。如果是,设置status_id=1。然后插入新记录。如果它已经存在,但不受第一个更新查询的影响,则忽略它(即,具有status_id=3的行)。

这很好地工作,但同时进行,它会在竞态条件下在重复键上崩溃。但是,如果我试图原子性地"插入不存在的地方",为什么会出现种族状况呢?


啊。我只是多搜索了一点,插入不存在的地方不是原子的。

引自http://www.postgresql.org/message-id/[email protected]

Mage writes:

The main question is that isn't"insert into ... select ... where not
exists" atomic?

不,它不是:它会在存在其他事务时失败执行相同的操作,因为exists测试将只看到在命令启动之前提交。你可能会读到手册中关于并发性的章节:http://www.postgresql.org/docs/9.0/static/mvc.html

< /块引用>