包含 insert into 子句的合并语句在 PostgreSQL 中失败
Coalesce sentence containing an insert into clause fails in PostgreSQL
这是我的琐事test
table,
create table test (
id int not null generated always as identity,
first_name. varchar,
primary key (id),
unique(first_name)
);
作为 insert-into-on-conflict
句子的替代方案,我试图使用 coalesce
惰性来尽可能执行 select
或 insert
,仅当 select
找不到一行。
coalesce
文档中描述了惰性。参见 https://www.postgresql.org/docs/current/functions-conditional.html
Like a CASE expression, COALESCE only evaluates the arguments that are needed to determine the result; that is, arguments to the right of the first non-null argument are not evaluated. This SQL-standard function provides capabilities similar to NVL and IFNULL, which are used in some other database systems.
我还想取回该行的 id
值,是否已插入。
我开始于:
select coalesce (
(select id from test where first_name='carlos'),
(insert into test(first_name) values('carlos') returning id)
);
但发现错误 syntax error at or near "into"
。
在另一个 DBFiddle 上看到它
https://www.db-fiddle.com/f/t7TVkoLTtWU17iaTAbEhDe/0
然后我尝试了:
select coalesce (
(select id from test where first_name='carlos'),
(with r as (
insert into test(first_name) values('carlos') returning id
) select id from r
)
);
这里我遇到了一个我不理解的 WITH clause containing a data-modifying statement must be at the top level
错误,因为 insert
是 with
.
中的第一个也是唯一一个句子
我正在使用 DBFiddle 和 PostgreSQL 13 对此进行测试。可以在以下位置找到源代码
https://www.db-fiddle.com/f/hp8T1iQ8eS4wozDCBhBXDw/5
不同方法:链接 CTE:
CREATE TABLE test
( id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, first_name VARCHAR UNIQUE
);
WITH sel AS (
SELECT id FROM test WHERE first_name = 'carlos'
)
, ins AS (
INSERT INTO test(first_name)
SELECT 'carlos'
WHERE NOT EXISTS (SELECT 1 FROM test WHERE first_name = 'carlos')
RETURNING id
)
, omg AS (
SELECT id FROM sel
UNION ALL
SELECT id FROM ins
)
SELECT id
FROM omg
;
这是对@wildplasser 接受的答案的改进。它避免比较 first_name
两次并使用 coalesce
而不是 union all
。有点像 selsert
一句话。
with sel as (
select id from test where first_name = 'carlos'
)
, ins as (
insert into test(first_name)
select 'carlos'
where (select id from sel) is null
returning id
)
select coalesce (
(select id from sel),
(select id from ins)
);
看来 insert into
子句的返回值在本质上不等同于 select
子句的标量查询。所以我尝试将 insert into
封装到一个 SQL 函数中并且它起作用了。
create or replace function insert_first_name(
_first_name varchar
) returns int
language sql as $$
insert into test (first_name)
values (_first_name)
returning id;
$$;
select coalesce (
(select id from test where first_name='carlos'),
(select insert_first_name('carlos'))
);
上查看
这是我的琐事test
table,
create table test (
id int not null generated always as identity,
first_name. varchar,
primary key (id),
unique(first_name)
);
作为 insert-into-on-conflict
句子的替代方案,我试图使用 coalesce
惰性来尽可能执行 select
或 insert
,仅当 select
找不到一行。
coalesce
文档中描述了惰性。参见 https://www.postgresql.org/docs/current/functions-conditional.html
Like a CASE expression, COALESCE only evaluates the arguments that are needed to determine the result; that is, arguments to the right of the first non-null argument are not evaluated. This SQL-standard function provides capabilities similar to NVL and IFNULL, which are used in some other database systems.
我还想取回该行的 id
值,是否已插入。
我开始于:
select coalesce (
(select id from test where first_name='carlos'),
(insert into test(first_name) values('carlos') returning id)
);
但发现错误 syntax error at or near "into"
。
在另一个 DBFiddle 上看到它
https://www.db-fiddle.com/f/t7TVkoLTtWU17iaTAbEhDe/0
然后我尝试了:
select coalesce (
(select id from test where first_name='carlos'),
(with r as (
insert into test(first_name) values('carlos') returning id
) select id from r
)
);
这里我遇到了一个我不理解的 WITH clause containing a data-modifying statement must be at the top level
错误,因为 insert
是 with
.
我正在使用 DBFiddle 和 PostgreSQL 13 对此进行测试。可以在以下位置找到源代码 https://www.db-fiddle.com/f/hp8T1iQ8eS4wozDCBhBXDw/5
不同方法:链接 CTE:
CREATE TABLE test
( id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, first_name VARCHAR UNIQUE
);
WITH sel AS (
SELECT id FROM test WHERE first_name = 'carlos'
)
, ins AS (
INSERT INTO test(first_name)
SELECT 'carlos'
WHERE NOT EXISTS (SELECT 1 FROM test WHERE first_name = 'carlos')
RETURNING id
)
, omg AS (
SELECT id FROM sel
UNION ALL
SELECT id FROM ins
)
SELECT id
FROM omg
;
这是对@wildplasser 接受的答案的改进。它避免比较 first_name
两次并使用 coalesce
而不是 union all
。有点像 selsert
一句话。
with sel as (
select id from test where first_name = 'carlos'
)
, ins as (
insert into test(first_name)
select 'carlos'
where (select id from sel) is null
returning id
)
select coalesce (
(select id from sel),
(select id from ins)
);
看来 insert into
子句的返回值在本质上不等同于 select
子句的标量查询。所以我尝试将 insert into
封装到一个 SQL 函数中并且它起作用了。
create or replace function insert_first_name(
_first_name varchar
) returns int
language sql as $$
insert into test (first_name)
values (_first_name)
returning id;
$$;
select coalesce (
(select id from test where first_name='carlos'),
(select insert_first_name('carlos'))
);
上查看