Return WHILE LOOP 部分后来自 PostgreSQL 函数的 SETOF 行
Return SETOF rows from PostgreSQL function after WHILE LOOP section
我正在尝试创建一个 POSTGRESQL 函数,该函数首先使用 WHILE LOOP
在 table 中 INSERT
一些数据,然后 SELECT
这个 [=30] 的结果=].
这是一个 sql 示例:
CREATE OR REPLACE FUNCTION get_levels_test (maxlevel int) RETURNS SETOF list_of_levels AS $$
DECLARE
level int = 1;
BEGIN
TRUNCATE list_of_levels;
WHILE (level <= maxlevel) LOOP
INSERT INTO list_of_levels
SELECT level;
level = level + 1;
END LOOP;
select * from list_of_levels;
END;
$$ LANGUAGE PLPGSQL VOLATILE;
然后我尝试调用这个函数:select get_levels_test (3)
,它显示了这个错误:
ERROR: query has no destination for result data
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Where: PL/pgSQL function "get_levels_test" line 12 at SQL statement
list_of_levels
table 仅包含一个 int
列。
如果需要,我使用的是 PostgreSQL 8.2.15(Greenplum 数据库 4.3.3.1 build 1)。
您需要使用 "return next",它可以与复合类型或 table 一起使用。请谨慎使用此功能,因为使用此功能可能会出现性能问题。不要使用它来将结果加入另一个 table。不要 return 大型数据集。仅将其用于非常小的结果,例如简短的报告。
您还需要在函数中添加异常处理。我将其添加到下面的示例函数中。
示例table:
create table employees
(id int not null,
manager_id int null,
employee_title text not null)
distributed by (id);
示例数据:
insert into employees values
(1, null, 'President'),
(2, 1, 'Vice-President'),
(3, 2, 'Manager'),
(4, 3, 'Engineer'),
(5, null, 'CMO'),
(6, 5, 'Director'),
(7, 6, 'Assistant'),
(8, 7, 'Developer');
函数:
create or replace function get_employees(p_id int) returns setof employees as
$$
declare
v_function_name text := 'get_employees';
v_location int;
v_rec record;
v_rec2 employees;
v_id employees.id%type;
begin
v_location := 1000;
create temporary table t1
(id integer) on commit drop distributed by (id);
v_location := 2000;
for v_rec in (select id from employees where id = p_id and manager_id is null order by id) loop
v_id := v_rec.id;
insert into t1 values (v_id);
while v_id is not null loop
select id into v_id from employees where manager_id = v_id;
if v_id is not null then
insert into t1 values (v_id);
end if;
end loop;
end loop;
v_location := 3000;
for v_rec2 in (select * from employees e join t1 on e.id = t1.id order by e.id) loop
return next v_rec2;
end loop;
exception
when others then
raise exception '(%:%:%)', v_function_name, v_location, sqlerrm;
end;
$$
language plpgsql;
并使用函数:
select * From get_employees(1);
id | manager_id | employee_title
----+------------+----------------
1 | | President
2 | 1 | Vice-President
3 | 2 | Manager
4 | 3 | Engineer
(4 rows)
select * From get_employees(5);
id | manager_id | employee_title
----+------------+----------------
5 | | CMO
6 | 5 | Director
7 | 6 | Assistant
8 | 7 | Developer
(4 rows)
我正在尝试创建一个 POSTGRESQL 函数,该函数首先使用 WHILE LOOP
在 table 中 INSERT
一些数据,然后 SELECT
这个 [=30] 的结果=].
这是一个 sql 示例:
CREATE OR REPLACE FUNCTION get_levels_test (maxlevel int) RETURNS SETOF list_of_levels AS $$
DECLARE
level int = 1;
BEGIN
TRUNCATE list_of_levels;
WHILE (level <= maxlevel) LOOP
INSERT INTO list_of_levels
SELECT level;
level = level + 1;
END LOOP;
select * from list_of_levels;
END;
$$ LANGUAGE PLPGSQL VOLATILE;
然后我尝试调用这个函数:select get_levels_test (3)
,它显示了这个错误:
ERROR: query has no destination for result data
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Where: PL/pgSQL function "get_levels_test" line 12 at SQL statement
list_of_levels
table 仅包含一个 int
列。
如果需要,我使用的是 PostgreSQL 8.2.15(Greenplum 数据库 4.3.3.1 build 1)。
您需要使用 "return next",它可以与复合类型或 table 一起使用。请谨慎使用此功能,因为使用此功能可能会出现性能问题。不要使用它来将结果加入另一个 table。不要 return 大型数据集。仅将其用于非常小的结果,例如简短的报告。
您还需要在函数中添加异常处理。我将其添加到下面的示例函数中。
示例table:
create table employees
(id int not null,
manager_id int null,
employee_title text not null)
distributed by (id);
示例数据:
insert into employees values
(1, null, 'President'),
(2, 1, 'Vice-President'),
(3, 2, 'Manager'),
(4, 3, 'Engineer'),
(5, null, 'CMO'),
(6, 5, 'Director'),
(7, 6, 'Assistant'),
(8, 7, 'Developer');
函数:
create or replace function get_employees(p_id int) returns setof employees as
$$
declare
v_function_name text := 'get_employees';
v_location int;
v_rec record;
v_rec2 employees;
v_id employees.id%type;
begin
v_location := 1000;
create temporary table t1
(id integer) on commit drop distributed by (id);
v_location := 2000;
for v_rec in (select id from employees where id = p_id and manager_id is null order by id) loop
v_id := v_rec.id;
insert into t1 values (v_id);
while v_id is not null loop
select id into v_id from employees where manager_id = v_id;
if v_id is not null then
insert into t1 values (v_id);
end if;
end loop;
end loop;
v_location := 3000;
for v_rec2 in (select * from employees e join t1 on e.id = t1.id order by e.id) loop
return next v_rec2;
end loop;
exception
when others then
raise exception '(%:%:%)', v_function_name, v_location, sqlerrm;
end;
$$
language plpgsql;
并使用函数:
select * From get_employees(1);
id | manager_id | employee_title
----+------------+----------------
1 | | President
2 | 1 | Vice-President
3 | 2 | Manager
4 | 3 | Engineer
(4 rows)
select * From get_employees(5);
id | manager_id | employee_title
----+------------+----------------
5 | | CMO
6 | 5 | Director
7 | 6 | Assistant
8 | 7 | Developer
(4 rows)