为什么子查询函数不插入新行?
Why subqueried function does not insert new rows?
我需要一个函数来插入行,因为一列的 (seriano
) 默认值应该与 PK 相同 id
.
我定义了table:
CREATE SEQUENCE some_table_id_seq
INCREMENT 1
START 1
MINVALUE 1
MAXVALUE 9223372036854775807
CACHE 1;
CREATE TABLE some_table
(
id bigint NOT NULL DEFAULT nextval('some_table_id_seq'::regclass),
itemid integer NOT NULL,
serialno bigint,
CONSTRAINT stockitem_pkey PRIMARY KEY (id),
CONSTRAINT stockitem_serialno_key UNIQUE (serialno)
);
和插入行数的函数:
CREATE OR REPLACE FUNCTION insert_item(itemid int, count int DEFAULT 1) RETURNS SETOF bigint AS
$func$
DECLARE
ids bigint[] DEFAULT '{}';
id bigint;
BEGIN
FOR counter IN 1..count LOOP
id := NEXTVAL( 'some_table_id_seq' );
INSERT INTO some_table (id, itemid, serialno) VALUES (id, itemid, id);
ids := array_append(ids, id);
END LOOP;
RETURN QUERY SELECT unnest(ids);
END
$func$
LANGUAGE plpgsql;
并且用它插入工作正常:
$ select insert_item(123, 10);
insert_item
-------------
1
2
3
4
5
6
7
8
9
10
(10 rows)
$ select * from some_table;
id | itemid | serialno
----+--------+----------
1 | 123 | 1
2 | 123 | 2
3 | 123 | 3
4 | 123 | 4
5 | 123 | 5
6 | 123 | 6
7 | 123 | 7
8 | 123 | 8
9 | 123 | 9
10 | 123 | 10
(10 rows)
但是如果我想使用函数 insert_item
作为子查询,它似乎不再起作用了:
$ select id, itemid from some_table where id in (select insert_item(123, 10));
id | itemid
----+--------
(0 rows)
我创建了哑函数 insert_dumb
以在子查询中进行测试:
CREATE OR REPLACE FUNCTION insert_dumb(itemid int, count int DEFAULT 1) RETURNS SETOF bigint AS
$func$
DECLARE
ids bigint[] DEFAULT '{}';
BEGIN
FOR counter IN 1..count LOOP
ids := array_append(ids, counter::bigint);
END LOOP;
RETURN QUERY SELECT unnest(ids);
END
$func$
LANGUAGE plpgsql;
这在子查询中按预期工作:
$ select id, itemid from some_table where id in (select insert_dumb(123, 10));
id | itemid
----+--------
1 | 123
2 | 123
3 | 123
4 | 123
5 | 123
6 | 123
7 | 123
8 | 123
9 | 123
10 | 123
(10 rows)
为什么 insert_item
函数在作为子查询调用时不插入新行?我尝试将 raise notice
添加到循环中,并且它每次都按预期运行 new id
(并增加序列),但没有新行附加到 table.
我将所有设置设为 fiddle
我在 Ubuntu.
上使用 Postgres 11
编辑
当然,我说出了我的真实原因,结果得到了回报...
我需要 insert_item
函数 returning id
s,所以我可以在 update
语句中使用它,例如:
update some_table set some_text = 'x' where id in (select insert_item(123, 10);)
除了为什么问题:可以理解我在 return 中得不到任何 ID(因为它们共享相同的快照),但是该函数运行所有需要的 INSERT 而没有 影响 table。这些行不应该在下一个查询中可用吗?
问题是子查询和周围查询共享同一个快照,也就是说,它们看到的是同一个数据库状态。因此外部查询看不到内部查询插入的行。
参见 the documentation(在 WITH
的上下文中进行了解释,尽管它也适用于此处):
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 (see Chapter 13), so they cannot “see” one another's effects on the target tables.
另外,你的做法还有第二个问题:如果你在语句上运行 EXPLAIN (ANALYZE)
,你会发现根本没有执行子查询!由于 table 为空,因此没有 id
,并且 运行 子查询不需要计算(空)结果。
您将不得不 运行 在两个不同的陈述中。或者,更好的是,以不同的方式进行:更新您刚刚插入的行是不必要的浪费。
Laurenz 解释了可见性问题,但如果您将函数重新编写为 return 实际 table,而不仅仅是他的 ID[,则根本不需要子查询
CREATE OR REPLACE FUNCTION insert_item(itemid int, count int DEFAULT 1)
RETURNS setof some_table
AS
$func$
INSERT INTO some_table (id, itemid, serialno)
select NEXTVAL( 'some_table_id_seq' ), itemid, currval('some_table_id_seq')
from generate_series(1,count)
returning *;
$func$
LANGUAGE sql;
那么你可以这样使用它:
select id, itemid
from insert_item(123, 10);
你得到了完整的插入行。
我需要一个函数来插入行,因为一列的 (seriano
) 默认值应该与 PK 相同 id
.
我定义了table:
CREATE SEQUENCE some_table_id_seq
INCREMENT 1
START 1
MINVALUE 1
MAXVALUE 9223372036854775807
CACHE 1;
CREATE TABLE some_table
(
id bigint NOT NULL DEFAULT nextval('some_table_id_seq'::regclass),
itemid integer NOT NULL,
serialno bigint,
CONSTRAINT stockitem_pkey PRIMARY KEY (id),
CONSTRAINT stockitem_serialno_key UNIQUE (serialno)
);
和插入行数的函数:
CREATE OR REPLACE FUNCTION insert_item(itemid int, count int DEFAULT 1) RETURNS SETOF bigint AS
$func$
DECLARE
ids bigint[] DEFAULT '{}';
id bigint;
BEGIN
FOR counter IN 1..count LOOP
id := NEXTVAL( 'some_table_id_seq' );
INSERT INTO some_table (id, itemid, serialno) VALUES (id, itemid, id);
ids := array_append(ids, id);
END LOOP;
RETURN QUERY SELECT unnest(ids);
END
$func$
LANGUAGE plpgsql;
并且用它插入工作正常:
$ select insert_item(123, 10);
insert_item
-------------
1
2
3
4
5
6
7
8
9
10
(10 rows)
$ select * from some_table;
id | itemid | serialno
----+--------+----------
1 | 123 | 1
2 | 123 | 2
3 | 123 | 3
4 | 123 | 4
5 | 123 | 5
6 | 123 | 6
7 | 123 | 7
8 | 123 | 8
9 | 123 | 9
10 | 123 | 10
(10 rows)
但是如果我想使用函数 insert_item
作为子查询,它似乎不再起作用了:
$ select id, itemid from some_table where id in (select insert_item(123, 10));
id | itemid
----+--------
(0 rows)
我创建了哑函数 insert_dumb
以在子查询中进行测试:
CREATE OR REPLACE FUNCTION insert_dumb(itemid int, count int DEFAULT 1) RETURNS SETOF bigint AS
$func$
DECLARE
ids bigint[] DEFAULT '{}';
BEGIN
FOR counter IN 1..count LOOP
ids := array_append(ids, counter::bigint);
END LOOP;
RETURN QUERY SELECT unnest(ids);
END
$func$
LANGUAGE plpgsql;
这在子查询中按预期工作:
$ select id, itemid from some_table where id in (select insert_dumb(123, 10));
id | itemid
----+--------
1 | 123
2 | 123
3 | 123
4 | 123
5 | 123
6 | 123
7 | 123
8 | 123
9 | 123
10 | 123
(10 rows)
为什么 insert_item
函数在作为子查询调用时不插入新行?我尝试将 raise notice
添加到循环中,并且它每次都按预期运行 new id
(并增加序列),但没有新行附加到 table.
我将所有设置设为 fiddle
我在 Ubuntu.
上使用 Postgres 11编辑
当然,我说出了我的真实原因,结果得到了回报...
我需要 insert_item
函数 returning id
s,所以我可以在 update
语句中使用它,例如:
update some_table set some_text = 'x' where id in (select insert_item(123, 10);)
除了为什么问题:可以理解我在 return 中得不到任何 ID(因为它们共享相同的快照),但是该函数运行所有需要的 INSERT 而没有 影响 table。这些行不应该在下一个查询中可用吗?
问题是子查询和周围查询共享同一个快照,也就是说,它们看到的是同一个数据库状态。因此外部查询看不到内部查询插入的行。
参见 the documentation(在 WITH
的上下文中进行了解释,尽管它也适用于此处):
The sub-statements in
WITH
are executed concurrently with each other and with the main query. Therefore, when using data-modifying statements inWITH
, the order in which the specified updates actually happen is unpredictable. All the statements are executed with the same snapshot (see Chapter 13), so they cannot “see” one another's effects on the target tables.
另外,你的做法还有第二个问题:如果你在语句上运行 EXPLAIN (ANALYZE)
,你会发现根本没有执行子查询!由于 table 为空,因此没有 id
,并且 运行 子查询不需要计算(空)结果。
您将不得不 运行 在两个不同的陈述中。或者,更好的是,以不同的方式进行:更新您刚刚插入的行是不必要的浪费。
Laurenz 解释了可见性问题,但如果您将函数重新编写为 return 实际 table,而不仅仅是他的 ID[,则根本不需要子查询
CREATE OR REPLACE FUNCTION insert_item(itemid int, count int DEFAULT 1)
RETURNS setof some_table
AS
$func$
INSERT INTO some_table (id, itemid, serialno)
select NEXTVAL( 'some_table_id_seq' ), itemid, currval('some_table_id_seq')
from generate_series(1,count)
returning *;
$func$
LANGUAGE sql;
那么你可以这样使用它:
select id, itemid
from insert_item(123, 10);
你得到了完整的插入行。