Ste*_*and 5 postgresql stored-procedures plpgsql postgresql-9.1 set-returning-functions
我在 Ubuntu 12.04 上使用 Postgresql 9.1。
在一个plpgsql函数中,我尝试连接setof从另一个函数返回的类型。
有type pair_id_value问题的创建与create type pair_id_value as (id bigint, value integer);
返回基本的函数setof pair_id_value(稍后将被连接的那些)是这样的:
create or replace function compute_pair_id_value(id bigint, value integer)
returns setof pair_id_value
as $$
listResults = []
for x in range(0,value+1):
listResults.append({ "id": id, "value": x})
return listResults
$$
language plpython3u;
Run Code Online (Sandbox Code Playgroud)
这个直截了当的 plpython 代码应该很好,例如查询:select * from compute_pair_id_value(1712437,2);返回很好:
id | value
---------------+-----------
1712437 | 0
1712437 | 1
1712437 | 2
(3 rows)
Run Code Online (Sandbox Code Playgroud)
对于这个例子,这个 python 函数现在相当简单,但最重要的是我的概念证明。在不久的将来,它会变得更加复杂。
当我尝试从多个 id 连接所有结果表时出现问题。
create or replace function compute_all_pair_id_value(id_obj bigint)
returns setof pair_id_value as $$
declare
pair pair_id_value;
begin
for pair in (select compute_pair_id_value(t.id, t.obj_value) from my_obj as t where t.id = id_obj)
loop
return next pair;
end loop;
return;
end; $$ language plpgsql;
Run Code Online (Sandbox Code Playgroud)
我收到错误:invalid input syntax for integer "(1712437,0)"好像它不再被视为具有两列的 pair_id_value,而是一个元组 (1712437,0)。
所以我将函数的输出类型从 setof pair_id_value 更改为 setof record ... 如果我执行这个类似的连接函数:
create or replace function compute_all_pair_id_value(id_obj bigint)
returns setof record as $$
declare
pair record;
begin
for pair in (select compute_pair_id_value(t.id, t.obj_value) from my_obj as t where t.id = id_obj)
loop
return next pair;
end loop;
return;
end; $$ language plpgsql;
Run Code Online (Sandbox Code Playgroud)
我收到错误: a column definition list is required for functions returning "record"
试图遵循这个 SO 问题的答案:我尝试以这种方式在 select 中定义列定义select compute_pair_id_value(t.id, t.obj_value) as f(id bigint, value integer),完整的代码在这里:
create or replace function compute_all_pair_id_value(id_obj bigint)
returns setof record as $$
declare
pair record;
begin
for pair in (select compute_pair_id_value(t.id, t.obj_value) as f(id bigint, value integer) from my_obj as t where t.id = id_obj)
loop
return next pair;
end loop;
return;
end; $$ language plpgsql;
Run Code Online (Sandbox Code Playgroud)
但是在启动 sql 脚本时,psql 不接受创建函数:
syntax error at or near "(" select compute_pair_id_value(t.id, t.obj_value) as f(id bigint, value integer)... 将手指指向 f(
知道如何正确地做到这一点吗?
我应该考虑创建临时表来完成这项工作吗?
您使用的方法过于复杂,而且效率很低。使用以下函数代替第一个函数:
create or replace function compute_pair_id_value(id bigint, value integer)
returns setof pair_id_value
as $$
SELECT $1, generate_series(0,$2);
$$
language sql;
Run Code Online (Sandbox Code Playgroud)
或者更好的是,完全摆脱它并像这样编写整个操作:
-- Sample data creation:
CREATE TABLE my_obj(id bigint, obj_value integer);
insert into my_obj(id,obj_value) VALUES (1712437,2),(17000,5);
-- and the query:
SELECT id, generate_series(0,obj_value) FROM my_obj;
Run Code Online (Sandbox Code Playgroud)
导致:
regress=> SELECT id, generate_series(0,obj_value) FROM my_obj;
id | generate_series
---------+-----------------
1712437 | 0
1712437 | 1
1712437 | 2
17000 | 0
17000 | 1
17000 | 2
17000 | 3
17000 | 4
17000 | 5
(9 rows)
Run Code Online (Sandbox Code Playgroud)
这利用了 PostgreSQL 在列表中调用的返回集合函数的行为SELECT。一旦 PostgreSQL 9.3 发布,它就可以被符合标准的LATERAL查询所取代。
由于事实证明您的问题是实际问题的简化版本,因此让我们解决这个问题。我将使用compute_pair_id_value上面的简化版本来避免 plpython3 的麻烦。以下是如何做你想做的事:
SELECT (compute_pair_id_value(id,obj_value)).* FROM my_obj;
Run Code Online (Sandbox Code Playgroud)
结果:
regress=> SELECT (compute_pair_id_value(id,obj_value)).* FROM my_obj;
id | value
---------+-------
1712437 | 0
1712437 | 1
1712437 | 2
17000 | 0
17000 | 1
17000 | 2
17000 | 3
17000 | 4
17000 | 5
(9 rows)
Run Code Online (Sandbox Code Playgroud)
但再次请注意,它将compute_pair_id_value被多次调用。这是 PostgreSQL 查询执行器的一个限制,可以在 9.3 中通过LATERAL支持来避免,但据我所知,在 9.2 及更低版本中您会遇到这种情况。观察:
create or replace function compute_pair_id_value(id bigint, value integer)
returns setof pair_id_value
as $$
BEGIN
RAISE NOTICE 'compute_pair_id_value(%,%)',id,value;
RETURN QUERY SELECT $1, generate_series(0,$2);
END;
$$
language plpgsql;
Run Code Online (Sandbox Code Playgroud)
输出:
regress=> SELECT (compute_pair_id_value(id,obj_value)).* FROM my_obj;
NOTICE: compute_pair_id_value(1712437,2)
NOTICE: compute_pair_id_value(1712437,2)
NOTICE: compute_pair_id_value(17000,5)
NOTICE: compute_pair_id_value(17000,5)
id | value
---------+-------
1712437 | 0
1712437 | 1
1712437 | 2
17000 | 0
17000 | 1
17000 | 2
17000 | 3
17000 | 4
17000 | 5
(9 rows)
Run Code Online (Sandbox Code Playgroud)
看看compute_pair_id_value每个输出列如何调用一次?
有一个解决方法:使用另一层子查询来解压复合类型结果。看:
regress=> SELECT (val).* FROM (SELECT compute_pair_id_value(id,obj_value) FROM my_obj) x(val);
NOTICE: compute_pair_id_value(1712437,2)
NOTICE: compute_pair_id_value(17000,5)
id | value
---------+-------
1712437 | 0
1712437 | 1
1712437 | 2
17000 | 0
17000 | 1
17000 | 2
17000 | 3
17000 | 4
17000 | 5
(9 rows)
Run Code Online (Sandbox Code Playgroud)
如果您确实必须LOOP超过结果,则可以在代码中使用相同的技术(这样做很慢,因此如果可以的话请避免使用)。
| 归档时间: |
|
| 查看次数: |
4156 次 |
| 最近记录: |