Using PLPGSQL function result in a join query - postgresql

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.

Related

How to join a table returned in a SQL operation with another table?

I would like to filter table X and then use an INNER JOIN to return a table which contains columns from table X and table Y. Here is the PostgreSQL code:
CREATE OR REPLACE FUNCTION player_data.GetAllTransactions(playerId INTEGER, transactionDate DATE)
RETURNS TABLE(id BIGINT, created_date TIMESTAMP WITH TIME ZONE, financial_transaction_type_id SMALLINT, amount NUMERIC)
LANGUAGE 'plpgsql'
AS
$$
DECLARE
BEGIN
RETURN QUERY SELECT db.x.id, db.x.created_date, db.x.trans_type, db.x.amount
FROM db.x
WHERE db.x.created_date::DATE=transactionDate;
-- INNER JOIN db.y ON db.y.id=db.x.wallet_id WHERE db.y.player_id=playerId;
END;
$$
I am trying to take the table from the SELECT statement and join it with the INNER JOIN statement. How can I achieve this?

How to return a table from iteration in function with postgres 11

Following functions are created for doing housekeeping within the database (PostgreSQL 11.4).
entity_with_multiple_taskexec: returns a list of entities for which the housekeeping should be done.
row_id_to_delete: returns tuples of id's to delete
Just for completeness, the function which works fine:
CREATE OR REPLACE FUNCTION entity_with_multiple_taskexec()
RETURNS TABLE(entitykey varchar) AS
$func$
BEGIN
RETURN QUERY select distinct task.entitykey from
(select task.entitykey from task where dtype = 'PropagationTask' group by task.entitykey having count(*) > (select count(*) from conninstance)) more_than_one_entry
inner join task on task.entitykey = more_than_one_entry.entitykey
inner join taskexec on taskexec.task_id = task.id order by task.entitykey asc;
END
$func$ LANGUAGE plpgsql;
But which the second function, I'm not able to return a table, created from looping through the results of the entity_with_multiple_taskexec function;
CREATE OR REPLACE FUNCTION row_id_to_delete()
RETURNS TABLE(task_id varchar, taskexec_id varchar) AS
$func$
DECLARE
entityrow RECORD;
resultset RECORD;
BEGIN
FOR entityrow IN SELECT entitykey FROM entity_with_multiple_taskexec() LOOP
insert into resultset select task.id as task_id, taskexec.id as taskexec_id from task
inner join taskexec on taskexec.task_id = task.id where taskexec.entitykey = entityrow.entitykey order by taskexec.enddate desc offset 1
END LOOP;
RETURN resultset;
END
$func$ LANGUAGE plpgsql;
This breaks with the following error
ERROR: syntax error at or near "END"
LINE 12: END LOOP;
I've tried different approaches. What would be a good solution to return the table?
You don't need a loop, just join to the function as if it is a table.
There is also no need to use PL/pgSQL for this, a simple language sql function will be more efficient.
CREATE OR REPLACE FUNCTION row_id_to_delete()
RETURNS TABLE(task_id varchar, taskexec_id varchar) AS
$func$
select task.id as task_id, taskexec.id as taskexec_id
from task
join taskexec on taskexec.task_id = task.id
join entity_with_multiple_taskexec() as mt on mt.entitykey = taskexec.entitykey
order by taskexec.enddate desc
offset 1
$func$
LANGUAGE sql;

PL/pgSQL: structure of query does not match function result type

I have a table
create table abac_object (
inbound abac_attribute,
outbound abac_attribute,
created_at timestamptz not null default now(),
primary key (inbound, outbound)
);
abac_attribute is my custom type that is defined like
create type abac_attribute as (
value text,
key text,
namespace_id uuid
);
I try to create a number of functions that work over the table
create function abac_object_list_1(_attr abac_attribute)
returns setof abac_object as $$
select *
from abac_object
where outbound = _attr
$$ language sql stable;
create function abac_object_list_2(_attr1 abac_attribute, _attr2 abac_attribute)
returns setof abac_object as $$
select t1.*
from abac_object as t1
inner join abac_object as t2 on t1.inbound = t2.inbound
where
t1.outbound = _attr1
and t2.outbound = _attr2
$$ language sql stable;
create function abac_object_list_3(_attr1 abac_attribute, _attr2 abac_attribute, _attr3 abac_attribute)
returns setof abac_object as $$
select t1.*
from abac_object as t1
inner join abac_object as t2 on t1.inbound = t2.inbound
inner join abac_object as t3 on t1.inbound = t3.inbound
where
t1.outbound = _attr1
and t2.outbound = _attr2
and t3.outbound = _attr3
$$ language sql stable;
create function abac_object_list(_attrs abac_attribute[], _offset integer, _limit integer)
returns setof abac_attribute as $$
begin
case array_length(_attrs, 1)
when 1 then return query
select t.inbound
from abac_object_list_1(_attrs[1]) as t
order by t.created_at
offset _offset
limit _limit;
when 2 then return query
select t.inbound
from abac_object_list_2(_attrs[1], _attrs[2]) as t
order by t.created_at
offset _offset
limit _limit;
when 3 then return query
select t.inbound
from abac_object_list_3(_attrs[1], _attrs[2], _attrs[3]) as t
order by t.created_at
offset _offset
limit _limit;
else raise exception 'bad argument' using detail = 'array length should be less or equal to 3';
end case;
end
$$ language plpgsql stable;
abac_object_list_1 works fine
select *
from abac_object_list_1(('apple', 'type', '00000000-0000-1000-a000-000000000010') ::abac_attribute);
However with abac_object_list I get the following error
select *
from abac_object_list(array [('apple', 'type', '00000000-0000-1000-a000-000000000010')] :: abac_attribute[], 0, 100);
[42804] ERROR: structure of query does not match function result type Detail: Returned type abac_attribute does not match expected type text in column 1. Where: PL/pgSQL function abac_object_list(abac_attribute[],integer,integer) line 4 at RETURN QUERY
I can't understand where text type comes from.
These functions worked before but then I added created_at column to be able to add explicit ORDER BY clause. And now I can't rewrite functions correctly.
Also maybe there is a better (or more idiomatic) way to define these function.
Could you please help me to figure it out?
Thank you.
P. S.
Well, it seems to work if I write
return query select (t.inbound).*
But I am not sure if it is a right approach.
Also I wonder what is better to use return table or return setof.

Merge multiple result tables and perform final query on result

I have a function returning table, which accumulates output of multiple calls to another function returning table. I would like to perform final query on built table before returning result. Currently I implemented this as two functions, one accumulating and one performing final query, which is ugly:
CREATE OR REPLACE FUNCTION func_accu(LOCATION_ID INTEGER, SCHEMA_CUSTOMER TEXT)
RETURNS TABLE("networkid" integer, "count" bigint) AS $$
DECLARE
GATEWAY_ID integer;
BEGIN
FOR GATEWAY_ID IN
execute format(
'SELECT id FROM %1$I.gateway WHERE location_id=%2$L'
, SCHEMA_CUSTOMER, LOCATION_ID)
LOOP
RETURN QUERY execute format(
'SELECT * FROM get_available_networks_gw(%1$L, %2$L)'
, GATEWAY_ID, SCHEMA_CUSTOMER);
END LOOP;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION func_query(LOCATION_ID INTEGER, SCHEMA_CUSTOMER TEXT)
RETURNS TABLE("networkid" integer, "count" bigint) AS $$
DECLARE
BEGIN
RETURN QUERY execute format('
SELECT networkid, max(count) FROM func_accu(%2$L, %1$L) GROUP BY networkid;'
, SCHEMA_CUSTOMER, LOCATION_ID);
END;
$$ LANGUAGE plpgsql;
How can this be done in single function, elegantly?
Both functions simplified and merged, also supplying value parameters in the USING clause:
CREATE OR REPLACE FUNCTION pg_temp.func_accu(_location_id integer, schema_customer text)
RETURNS TABLE(networkid integer, count bigint) AS
$func$
BEGIN
RETURN QUERY EXECUTE format('
SELECT f.networkid, max(f.ct)
FROM %I.gateway g
, get_available_networks_gw(g.id, $1) f(networkid, ct)
WHERE g.location_id = $2
GROUP BY 1'
, _schema_customer)
USING _schema_customer, _location_id;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM func_accu(123, 'my_schema');
Related:
Dynamically access column value in record
I am using alias names for the columns returned by the function (f(networkid, ct)) to be sure because you did not disclose the return type of get_available_networks_gw(). You can use the column names of the return type directly.
The comma (,) in the FROM clause is short syntax for CROSS JOIN LATERAL .... Requires Postgres 9.3 or later.
What is the difference between LATERAL and a subquery in PostgreSQL?
Or you could run this query instead of the function:
SELECT f.networkid, max(f.ct)
FROM myschema.gateway g, get_available_networks_gw(g.id, 'my_schema') f(networkid, ct)
WHERE g.location_id = $2
GROUP BY 1;

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