我已经实现了这样的简单更新/插入查询:
-- 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
。
然后插入新记录。如果它已经存在,但没有受到第一个UPDATE查询的影响,请忽略它(即带有status_id=3
的行。)
这很好用,但同时执行它会在竞争条件下重复键崩溃。 但是,为什么竞争条件会发生,如果我试图以原子方式“插入 - 不存在”?
答案 0 :(得分:2)
阿。我只是搜索了一点,插入不存在的地方不是原子。
引自http://www.postgresql.org/message-id/26970.1296761016@sss.pgh.pa.us:
法师写道:
主要问题是不是“插入......选择......不是 存在“原子?
不,它不是: 会在其他交易存在的情况下失败 做同样的事情,因为EXISTS测试只会看到行 在命令启动之前提交。你可能会关心阅读 手册关于并发的章节: http://www.postgresql.org/docs/9.0/static/mvcc.html