从条件 INSERT 中获取 Id
Get Id from a conditional INSERT
对于像这样的 table:
CREATE TABLE Users(
id SERIAL PRIMARY KEY,
name TEXT UNIQUE
);
以下操作的正确单查询插入是什么:
给定一个用户 name
,插入一条新记录和 return 新的 id
。但是,如果 name
已经存在,则 return id
.
我知道 PostgreSQL 9.5 中用于 ON CONFLICT(column) DO UPDATE/NOTHING
的新语法,但我不知道它有什么帮助,如果有的话,考虑到我需要 id
被 return 编辑。
看来RETURNING id
和ON CONFLICT
不属于一起
对于单行插入且无更新:
with i as (
insert into users (name)
select 'the name'
where not exists (
select 1
from users
where name = 'the name'
)
returning id
)
select id
from users
where name = 'the name'
union all
select id from i
The manual 关于主查询和 with
子查询部分:
The primary query and the WITH queries are all (notionally) executed at the same time
虽然这听起来对我来说 "same snapshot" 我不确定,因为我不知道名义上 在那种情况下意味着什么。
The sub-statements in WITH are executed concurrently with each other and with the main query. Therefore, when using data-modifying statements in WITH, the order in which the specified updates actually happen is unpredictable. All the statements are executed with the same snapshot
如果我理解正确的话 same snapshot 位可以防止竞争条件。但是我再次不确定 all the statements 它是否仅指 with
子查询中的语句,不包括主查询。为避免任何疑问,将上一个查询中的 select 移动到 with
子查询:
with s as (
select id
from users
where name = 'the name'
), i as (
insert into users (name)
select 'the name'
where not exists (select 1 from s)
returning id
)
select id from s
union all
select id from i
UPSERT 实现非常复杂,无法防止并发写访问。查看在初始开发期间用作日志的 this Postgres Wiki。 Postgres 黑客决定不在 Postgres 9.5 的第一个版本的 RETURNING
子句中包含 "excluded" 行。他们可能会为下一个版本构建一些东西。
这是手册中解释您的情况的关键语句:
The syntax of the RETURNING
list is identical to that of the output
list of SELECT
. Only rows that were successfully inserted or updated
will be returned. For example, if a row was locked but not updated
because an ON CONFLICT DO UPDATE ... WHERE
clause condition was not
satisfied, the row will not be returned.
大胆强调我的。
对于单行插入:
同一个 table
上没有并发写入负载
WITH ins AS (
INSERT INTO users(name)
VALUES ('new_usr_name') -- input value
ON CONFLICT(name) DO NOTHING
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;
table
上可能存在并发写入负载
考虑一下(对于 单行 INSERT
):
- Is SELECT or INSERT in a function prone to race conditions?
插入行集:
三个都有非常详细的解释。
对于像这样的 table:
CREATE TABLE Users(
id SERIAL PRIMARY KEY,
name TEXT UNIQUE
);
以下操作的正确单查询插入是什么:
给定一个用户 name
,插入一条新记录和 return 新的 id
。但是,如果 name
已经存在,则 return id
.
我知道 PostgreSQL 9.5 中用于 ON CONFLICT(column) DO UPDATE/NOTHING
的新语法,但我不知道它有什么帮助,如果有的话,考虑到我需要 id
被 return 编辑。
看来RETURNING id
和ON CONFLICT
不属于一起
对于单行插入且无更新:
with i as (
insert into users (name)
select 'the name'
where not exists (
select 1
from users
where name = 'the name'
)
returning id
)
select id
from users
where name = 'the name'
union all
select id from i
The manual 关于主查询和 with
子查询部分:
The primary query and the WITH queries are all (notionally) executed at the same time
虽然这听起来对我来说 "same snapshot" 我不确定,因为我不知道名义上 在那种情况下意味着什么。
The sub-statements in WITH are executed concurrently with each other and with the main query. Therefore, when using data-modifying statements in WITH, the order in which the specified updates actually happen is unpredictable. All the statements are executed with the same snapshot
如果我理解正确的话 same snapshot 位可以防止竞争条件。但是我再次不确定 all the statements 它是否仅指 with
子查询中的语句,不包括主查询。为避免任何疑问,将上一个查询中的 select 移动到 with
子查询:
with s as (
select id
from users
where name = 'the name'
), i as (
insert into users (name)
select 'the name'
where not exists (select 1 from s)
returning id
)
select id from s
union all
select id from i
UPSERT 实现非常复杂,无法防止并发写访问。查看在初始开发期间用作日志的 this Postgres Wiki。 Postgres 黑客决定不在 Postgres 9.5 的第一个版本的 RETURNING
子句中包含 "excluded" 行。他们可能会为下一个版本构建一些东西。
这是手册中解释您的情况的关键语句:
The syntax of the
RETURNING
list is identical to that of the output list ofSELECT
. Only rows that were successfully inserted or updated will be returned. For example, if a row was locked but not updated because anON CONFLICT DO UPDATE ... WHERE
clause condition was not satisfied, the row will not be returned.
大胆强调我的。
对于单行插入:
同一个 table
上没有并发写入负载WITH ins AS (
INSERT INTO users(name)
VALUES ('new_usr_name') -- input value
ON CONFLICT(name) DO NOTHING
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;
table
上可能存在并发写入负载考虑一下(对于 单行 INSERT
):
- Is SELECT or INSERT in a function prone to race conditions?
插入行集:
三个都有非常详细的解释。