POSTGRESQL convert recursive function to loop function - postgresql

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

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;

Using PLPGSQL function result in a join query

I am developing an application its database is Postgres 9.5
I am having the following plpgsql function
CREATE OR REPLACE FUNCTION public.getall_available_products(
IN start_day_id integer,
IN end_day_id integer)
RETURNS TABLE(id integer) AS
$BODY$
SELECT product_id As id
FROM product_days
WHERE available > 0
AND days_id BETWEEN start_day_id AND end_day_id
$BODY$
LANGUAGE sql VOLATILE
I need to use the result of the above function in a join query in another plpgsql
function
CREATE OR REPLACE FUNCTION public.get_available_product_details(
IN start_day_id integer,
IN end_day_id integer)
RETURNS SETOF record AS
$BODY$declare
begin
SELECT pd.days_id As pd_days_id, pd.id AS p_id, pd.name AS p_name
FROM product p JOIN product_days pd
Using(id)
WHERE pd.id in
Select * from
//here I need to use the result of the getall_available_products
//function
end;
$BODY$
LANGUAGE plpgsql VOLATILE
How should I use the result of the first function in the second function? where I specify with comments.
You can select from set / table returning functions like tables or views. In your case:
SELECT pd.days_id As pd_days_id, pd.id AS p_id, pd.name AS p_name
FROM product p JOIN product_days pd USING(id)
WHERE pd.id IN
(SELECT a.id FROM public.getall_available_products(start_day_id, end_day_id) a);
You may even join with functions:
SELECT pd.days_id As pd_days_id, pd.id AS p_id, pd.name AS p_name
FROM product p JOIN product_days pd USING(id)
JOIN public.getall_available_products(start_day_id, end_day_id) a ON pd.id = a.id;
This should give the same result.
Note: If you want pass column values as function arguments you should take a look at the relatively new keyword LATERAL.

Is there a functional form of union using a set or array in Postgresql

I have an SET of id's in a WHERE statement that gives me valid seasonal days for certain taxa id's
WHERE
...
tx_id IN ('00020','00030','00059') AND
datepart('doy',dt) IN (
SELECT TAXA_SEASON(1,'00020') UNION
SELECT TAXA_SEASON(1,'00030') UNION
SELECT TAXA_SEASON(1,'00059') )
and tax_sesion is a function for the 4 seasons I can select.
CREATE OR REPLACE FUNCTION taxa_season(SEAS INTEGER, EURING TEXT)
RETURNS SETOF INTEGER AS
$BODY$
...
$BODY$
LANGUAGE plpgsql;
Is there an functional form of union using a set or array in Postgresql
datepart('doy',dt) IN (SELECT TAXA_SEASION(1,{'00020','00030','00059'}) )
CREATE OR REPLACE FUNCTION taxa_season(SEAS INTEGER, EURINGS TEXT[])
RETURNS SETOF INTEGER AS
$BODY$
DECLARE
E TEXT;
BEGIN
FOREACH E IN ARRAY EURINGS LOOP
RETURN QUERY SELECT TAXA_SEASON(SEAS, E);
END LOOP;
RETURN;
END
$BODY$
LANGUAGE plpgsql;
Usage:
WHERE
...
tx_id = ANY(ARRAY['00020','00030','00059']) AND
datepart('doy',dt) IN (SELECT TAXA_SEASON(1,ARRAY['00020','00030','00059']) )
Upd: It was "lazy" solution does not required the changes in the existing code. The right solution is to invert the logic:
Create functions like:
CREATE OR REPLACE FUNCTION taxa_season(SEAS INTEGER, EURING TEXT[])
RETURNS SETOF INTEGER AS
$BODY$
-- Get ready to use (distinct) data for all values from EURING
$BODY$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION taxa_season(SEAS INTEGER, EURING TEXT)
RETURNS SETOF INTEGER AS
$BODY$
select taxa_season(SEAS, ARRAY[EURING])
$BODY$
LANGUAGE sql; -- Note that it is simple SQL function
It would be more efficiency.
Thanks #Abelisto finally it helps me out and the whole construct looks like this..
-- --------------------------------------------------------
CREATE OR REPLACE FUNCTION taxa_season(SEAS INTEGER, EURING TEXT)
RETURNS SETOF INTEGER AS
$BODY$
...find some date limits in the taxa tables
$BODY$
LANGUAGE plpgsql;
-- --------------------------------------------------------
CREATE OR REPLACE FUNCTION taxa_season(SEAS INTEGER, EURINGS TEXT[])
RETURNS SETOF INTEGER AS
$BODY$
DECLARE
E TEXT;
BEGIN
FOREACH E IN ARRAY EURINGS LOOP
RETURN QUERY SELECT TAXA_SEASON(SEAS, E);
END LOOP;
RETURN;
END
$BODY$
LANGUAGE plpgsql;
-- ----------------------------------------------
CREATE OR REPLACE FUNCTION sessions_taxa_season(
YR INTEGER,
SEAS INTEGER,
LOC TEXT,
EURINGS TEXT[])
RETURNS SETOF TEXT AS
$BODY$
DECLARE
R RECORD;
BEGIN
IF SEAS > 0 AND SEAS < 4 THEN
FOR R IN
SELECT DISTINCT session
FROM sync_utm32
WHERE
date_part('doy', gps_dt)
IN (SELECT DISTINCT TAXA_SEASON(SEAS, EURINGS)) AND
date_part('year', gps_dt) = YR AND
session ~~ LOC
ORDER BY SESSION
LOOP
RETURN NEXT R.SESSION;
END LOOP;
END IF;
IF SEAS=4 THEN
FOR R IN
SELECT DISTINCT SESSION
FROM sync_utm32
WHERE
date_part('doy', gps_dt)
IN (SELECT DISTINCT TAXA_SEASON(SEAS, EURINGS)) AND
date_part('year', gps_dt) = YR-1 AND
date_part('doy', gps_dt) >= 365/2 AND
session ~~ LOC
ORDER BY SESSION
LOOP
RETURN NEXT R.SESSION;
END LOOP;
FOR R IN
SELECT DISTINCT SESSION
FROM sync_utm32
WHERE
date_part('doy', gps_dt)
IN (SELECT DISTINCT TAXA_SEASON(SEAS, EURINGS)) AND
date_part('year', gps_dt) = YR AND
date_part('doy', gps_dt) < 365/2 AND
session ~~ LOC
LOOP
RETURN NEXT R.SESSION;
END LOOP;
END IF;
RETURN;
END
$BODY$
LANGUAGE plpgsql;
Result of request:
SELECT SESSIONS_TAXA_SEASON(2016, 4,'%XX',ARRAY['00020','00030','00059']);
sessions_taxa_season
----------------------
2015-11-01-XX
2015-12-07-XX
2016-02-16-XX
2016-01-21-XX
The rest is sprintf template driven from perl DBI.

Use Variable in a select statement in a Function

Is it possible to declare a variable from another variable that was already declared in a function?
Example of what I am expecting below..
CREATE OR REPLACE FUNCTION insertRecord
(
a varchar (100),
b varchar(100),
c varchar(100)
)
RETURNS TEXT AS $$
DECLARE
orgId := (select id from org)
projectId := select id from project where orgId = orgId
BEGIN
return projectId;
END;
$$ LANGUAGE plpgsql;
First declare the variables. And rename the orgId to avoid ambiguity with the column name.
CREATE OR REPLACE FUNCTION insertRecord
(
a varchar (100),
b varchar(100),
c varchar(100)
)
RETURNS TEXT AS $$
DECLARE
orgId_var integer;
projectId varchar;
BEGIN
orgId_var := (select id from org limit 1);
projectId := (select id from project where orgId = orgId_var);
return projectId;
END;
$$ LANGUAGE plpgsql;
Perhaps you just want to use a join instead? Something along these lines (but with more filters added so as not to return multiple rows):
CREATE OR REPLACE FUNCTION insertRecord(
a varchar (100),
b varchar(100),
c varchar(100)
)
RETURNS TEXT AS $$
DECLARE
v_project_id project.id%TYPE;
BEGIN
SELECT p.id
INTO v_project_id
FROM project p
JOIN org o ON (o.id = p.org_id);
RETURN v_project_id;
END;
$$ LANGUAGE plpgsql;

Greenplum/Postgres 8 function dynamic result set?

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