Greenplum/Postgres 8 function dynamic result set? - postgresql

I need to write a function that returns a table with unknown number of columns.
If i receive 'None' in column input parameter then that column shouldn't be included in the output. In postgres 9+ there is a solution for this problem.
something like below:
CREATE OR REPLACE FUNCTION data_of(id integer,col1 varchar,col2 varchar, col3 varchar)
RETURNS TABLE (count_rec, dimensions text[] ) AS
$func$
DECLARE
_dimensions text := 'col1, col2, col3'; -- If i receive 'None' in input param then i exclude that from column list
BEGIN
RETURN QUERY EXECUTE format('
SELECT count(*) as count_rec,
string_to_array($1) -- AS dimensions
FROM x
WHERE id = $2'
, _dimensions)
USING _dimensions , _id;
END
$func$ LANGUAGE plpgsql;
But in Greenplum (Postgres 8.2) i could not find any. Is there any similar solution?
thanks

You have 2 options to do it: use set-returning function returning "record" or returning your custom type.
First option:
create table test (a int, b int, c int, d varchar, e varchar, f varchar);
insert into test select id, id*2, id*3, (id*4)::varchar, (id*4)::varchar, (id*4)::varchar from generate_series(1,10) id;
create or replace function test_func(column_list varchar[]) returns setof record as $BODY$
declare
r record;
begin
for r in execute 'select ' || array_to_string(column_list, ',') || ' from test' loop
return next r;
end loop;
return;
end;
$BODY$
language plpgsql
volatile;
select * from test_func(array['a','c','e']) as f(a int, c int, e varchar);
Second option:
create table test (a int, b int, c int, d varchar, e varchar, f varchar);
insert into test select id, id*2, id*3, (id*4)::varchar, (id*4)::varchar, (id*4)::varchar from generate_series(1,10) id;
create type testtype as (
a int,
c int,
e varchar
);
create or replace function test_func() returns setof testtype as $BODY$
declare
r testtype;
begin
for r in execute 'select a,c,e from test' loop
return next r;
end loop;
return;
end;
$BODY$
language plpgsql
volatile;
select * from test_func();
But I'm 99% sure you're trying to do something wrong. In Greenplum the result of function execution cannot be used as a "table" in join conditions, because the function executes on the master. You even won't be able to create a table out of the last query returning the data from your function because of this limitation
In short, this is not a recommended way to work with data in Greenplum

Related

Avoid 'ambiguous column' in simple PostgreSQL function returning table

Suppose I have the following table, function and execution:
create table mytable (a INTEGER, b INTEGER);
create function test(q INTEGER)
returns table(a INTEGER, b INTEGER)
as
$body$
begin
return query select a,b from mytable;
end;
$body$
language plpgsql STABLE;
select * from test(1);
I get an 'ambiguous column name' error. I can get rid of it by changing the selection to "select t.a, t.b from mytable t" (per some similar-ish posts). But it seems very odd to have to qualify the column names when there is only 1 table in my query. I'm porting code that has quite a lot of stored procedures selecting from single tables (in various ways) and returning a table with columns that have the same name. Is there a better way than this of avoiding the error, and still having an output table with the same column names?
Thanks for any leads.
you can (you should) to use aliases.
create table mytable (a INTEGER, b INTEGER);
create function test(q INTEGER)
returns table(a INTEGER, b INTEGER)
as
$body$
begin
return query select mt.a, mt.b from mytable mt;
end;
$body$
language plpgsql STABLE;
select * from test(1);
maybe thats an option, using format and query execute:
( as the error says, it doesnt know which a to take, the pl/pgSQL variable or a columname )
create function test(q INTEGER)
returns table(a INTEGER, b INTEGER)
as
$body$
declare
lsQueryExecute text;
begin
lsQueryExecute = format('select a,b from mytable');
return query execute lsQueryExecute;
end;
$body$
language plpgsql STABLE;

How to do postgresql select query funciton using parameter?

I want to create a postgresql funciton that returns records. But if I pass an id parameter, it should be add in where clause. if I do not pass or null id parameter, where clasuse will not add the query.
CREATE OR REPLACE FUNCTION my_func(id integer)
RETURNS TABLE (type varchar, total bigint) AS $$
DECLARE where_clause VARCHAR(200);
BEGIN
IF id IS NOT NULL THEN
where_clause = ' group_id= ' || id;
END IF ;
RETURN QUERY SELECT
type,
count(*) AS total
FROM
table1
WHERE
where_clause ???
GROUP BY
type
ORDER BY
type;
END
$$
LANGUAGE plpgsql;
You can either use one condition that takes care of both situations (then you don't need PL/pgSQL to begin with):
CREATE OR REPLACE FUNCTION my_func(p_id integer)
RETURNS TABLE (type varchar, total bigint)
AS $$
SELECT type,
count(*) AS total
FROM table1
WHERE p_id is null or group_id = p_id
GROUP BY type
ORDER BY type;
$$
LANGUAGE sql;
But an OR condition like that is typically not really good for performance. The second option you have, is to simply run two different statements:
CREATE OR REPLACE FUNCTION my_func(p_id integer)
RETURNS TABLE (type varchar, total bigint)
AS $$
begin
if (p_id is null) then
return query
SELECT type,
count(*) AS total
FROM table1
GROUP BY type
ORDER BY type;
else
return query
SELECT type,
count(*) AS total
FROM table1
WHERE group_id = p_id
GROUP BY type
ORDER BY type;
end if;
END
$$
LANGUAGE plgpsql;
And finally you can build a dynamic SQL string depending the parameter:
CREATE OR REPLACE FUNCTION my_func(p_id integer)
RETURNS TABLE (type varchar, total bigint)
AS $$
declare
l_sql text;
begin
l_sql := 'SELECT type, count(*) AS total FROM table1 '
if (p_id is not null) then
l_sql := l_sql || ' WHERE group_id = '||p_id;
end if;
l_sql := l_sql || ' GROUP BY type ORDER BY type';
return query execute l_sql;
end;
$$
LANGUAGE plpgsql;
Nothing is required just to use the variable as it is for more info please refer :plpgsql function parameters

POSTGRESQL convert recursive function to loop function

CREATE OR REPLACE FUNCTION employee(IN emp_id integer)
RETURNS TABLE(id integer, name text, designation text, salary integer, man_id integer) AS
$BODY$
BEGIN
RETURN QUERY
WITH recursive manager_hierarchy(e_id,e_name,e_desig,e_sal,m_id) AS
(
select e.id,e.name,e.designation,e.salary,e.man_id FROM emp_table e
WHERE e.id=emp_id
union
SELECT
rp.id,rp.name,rp.designation,rp.salary,rp.man_id
FROM
manager_hierarchy mh
INNER JOIN emp_table rp ON mh.e_id=rp.man_id
)
SELECT h.e_id,h.e_name,h.e_desig,h.e_sal,h.m_id
FROM manager_hierarchy h;
END;
$BODY$
LANGUAGE plpgsql
can anyone help me to change the same program with using loop

Update table being returned in PostgreSQL

I'm looking for help with a PostgreSQL function that returns a table.
I would like to know if there is a way of updating the table that is being returned.
Here's an example,
create or replace function fn_function()
return table(column01 integer, column02 integer, column03 boolean) as $$
return query
select col1, col2, false
from tableXYZ;
/* how can i update the table, let's say column03 before the function exits */
end
$$ language plpgsql;
Can i give an alias to the table being returned?
The postgre version in use is 9.0.8.
Thx in advance.
Here is a set based solution.
The RETURNS TABLE is just portal to get data out you can't do anything inside the function that defines it. You could create another function and call this one and do things to the result set. However I use a temp table so you can manipulate the data before you send it out.
CREATE OR REPLACE FUNCTION fn_function()
RETURNS TABLE(column01 integer, column02 integer, column03 boolean) AS $$
BEGIN
CREATE TEMP TABLE temp_tableXYZ (column01 integer, column02 integer, column03 boolean) ON COMMIT DROP;
INSERT INTO temp_tableXYZ (column01, column02, column03)
SELECT col1,col2,col3
FROM tableXYZ;
--WHERE filter if you can.
UPDATE temp_tableXYZ
SET col1 = 9999;
RETURN QUERY select column01, column02, column03 from temp_tableXYZ;
END;
$$ LANGUAGE plpgsql;
You can call it using an alias like this:
SELECT * FROM fn_function() as my_table;
Just do a query which returns the values you want. It can be plain sql:
create or replace function fn_function()
returns table (
column01 integer, column02 integer, column03 boolean
) as $$
select col1, col2, col2 > 10
from tableXYZ;
$$ language sql;
In the example above column 3 will be true if col2 > 10 and false otherwise. Another example using a subselect:
create or replace function fn_function()
returns table (
column01 integer, column02 integer, column03 boolean
) as $$
select col1, col2, (select max(col1) > 10 from t where col2 = tableXYZ.col1)
from tableXYZ;
$$ language sql;
Notice that it is not return but returns
To select from table, modify the results of select and pass them as results of a function try something like:
create or replace function fn_function()
returns table (
column01 integer, column02 integer, column03 boolean
) as $$
begin
for rec in select col1, col2, false as col3
from tableXYZ;
loop
rec.col3 := col1 > col2;
return next rec;
end loop;
return;
end
$$ language plpgsql;
Details here.
To select get the results of this function simply
SELECT function_alias.col1, function_alias.col2
FROM fn_function() function_alias;
You can do with this function same things you can do with normal tables.

Postgres dynamic sql and list result

I used EXECUTE(for dynamic sql) and SETOF(result is returning as list), but it is the wrong :(
create table test as
select 1 id, 'safd' data1,'sagd' data2
union
select 2 id, 'hdfg' data1,'sdsf' data2;
create or replace function test2(a varchar) returns SETOF record as
$BODY$
declare x record;
begin
for x in execute a loop
RETURN NEXT x;
end loop;
return;
end;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
select * from test2('select * from test');
You will have to know in advance the structure of the returned record
select * from test2('select * from test') s(a int, b text, c text);
a | b | c
---+------+------
1 | safd | sagd
2 | hdfg | sdsf
Or if the returned set will always be a set of the test table then use the Akash's proposed solution.
replace
create or replace function test2(a varchar) returns SETOF RECORD as
with
create or replace function test2(a varchar) returns SETOF test as
^^^^ name of table (it specifies the datatypes of the set)
You need to add some OUT params.
CREATE FUNCTION test2(a character varying, OUT id integer, OUT data1 text, OUT data2 text) RETURNS SETOF record
LANGUAGE plpgsql
AS $$
begin
RETURN QUERY EXECUTE a;
end;
$$;