vit*_*y-t 8 sql postgresql concurrency upsert
对于像这样的表:
CREATE TABLE Users(
id SERIAL PRIMARY KEY,
name TEXT UNIQUE
);
Run Code Online (Sandbox Code Playgroud)
以下操作的正确单一查询插入是什么:
给定用户name
,插入新记录并返回新记录id
.但如果name
已经存在,只需返回id
.
我知道PostgreSQL 9.5中的新语法ON CONFLICT(column) DO UPDATE/NOTHING
,但我无法弄清楚,如果有的话,它可以提供帮助,因为我需要id
返回.
这似乎RETURNING id
与ON CONFLICT
不属于一起.
UPSERT实现非常复杂,可以安全地防止并发写访问.看一下在初始开发过程中用作日志的Postgres Wiki.Postgres黑客决定不在RETURNING
Postgres 9.5的第一个版本的条款中包含"排除"行.他们可能会为下一个版本构建一些内容.
这是手册中解释您情况的关键声明:
RETURNING
列表的语法与输出列表的语法相同SELECT
.仅返回已成功插入或更新的行.例如,如果一行被锁定但由于ON CONFLICT DO UPDATE ... WHERE
未满足子句条件而未更新,则不会返回该行.
大胆强调我的.
对于单行插入:
WITH ins AS (
INSERT INTO users(name)
VALUES ('new_usr_name') -- input value
ON CONFLICT(name) DO UPDATE
SET name = name WHERE FALSE -- never executed, just to lock row
RETURNING users.id
)
SELECT id FROM ins
UNION ALL
SELECT id FROM users -- 2nd SELECT never executed if INSERT successful
WHERE name = 'new_usr_name' -- input value a 2nd time
LIMIT 1;
Run Code Online (Sandbox Code Playgroud)
或者包装成一个函数,只提供一次新名称.就像在这里演示的那样(也考虑解释LIMIT 1
):
可能的竞争:并发事务可能会更改/删除INSERT
尝试与之间的现有行SELECT
.极不可能,但可能.
如果您没有(可能的)并发写访问权限(或者只是不关心),请简化:
...
ON CONFLICT(name) DO NOTHING
...
Run Code Online (Sandbox Code Playgroud)
要插入一组行:
归档时间: |
|
查看次数: |
1893 次 |
最近记录: |