How can create one function from two functions in PostgreSQL? - postgresql

I would like to create another function in PostgreSQL, function_c, from function_a and function_b.
function_a
CREATE OR REPLACE FUNCTION function_a(year_ INTEGER, col_b INTEGER)
RETURNS INTEGER AS
$$
DECLARE
vbl INTEGER;
BEGIN
SELECT COUNT(col_b) INTO vbl
FROM table1
WHERE date_part('year',table1.col_aa) = year_ AND table1.col_bb = col_b;
RETURN vbl;
END;
$$
LANGUAGE plpgsql;
function_b
CREATE OR REPLACE FUNCTION function_b(year_ INTEGER)
RETURNS INTEGER AS
$$
DECLARE
vbl_b integer;
BEGIN
SELECT COUNT(col_aa) INTO vble_b
FROM table1
WHERE date_part('year',table1.col_aa) = year_;
RETURN vbl_b
END;
$$
LANGUAGE plpgsql;
I want to create function c that including function a and b, returns:
year: year_ parameter of function_b
median: vbl of function_a divided vbl_b of function_b
How can create one function from two functions in PostgreSQL?

If I understood correctly below mentioned definition will work for you
CREATE OR REPLACE FUNCTION function_c(year_ INTEGER, col_b INTEGER)
RETURNS table (year1 integer, median numeric) AS
$$
DECLARE
var1 INTEGER;
var2 INTEGER;
var3 numeric;
BEGIN
var1=(select * from function_a(year_));
var2=(select * from function_b(year_, vol_b));
var3=var1/var2;
return query
select year_, var3;
END;
$$
LANGUAGE plpgsql;

Related

Postgresql howto dynamically change parameter value

Is it possible to dynamically set param of numeric() ? eg:
CREATE OR REPLACE somefunction() RETURNS numeric AS
DECLARE
f numeric;
x integer;
BEGIN
x := 2;
SELECT INTO f CAST(something AS numeric(12, x));
RETURN f;
END;
So, I don't need to use CASE inside my plpgsql function if possible :) Tried everything, but it does not work, cast expects constant. Thanks:)
Using Dynamic query:
CREATE OR REPLACE FUNCTION public.somefunction(something numeric)
RETURNS numeric
LANGUAGE plpgsql
AS $function$
DECLARE
f numeric;
x integer;
BEGIN
x := 2;
EXECUTE 'SELECT CAST($1 AS numeric(12, ' || x ||'))' INTO f USING something;
RETURN f;
END;
$function$
;
select somefunction(126.787);
somefunction
--------------
126.79
Alternate where you pass in the scale:
CREATE OR REPLACE FUNCTION public.somefunction(something numeric, scale integer)
RETURNS numeric
LANGUAGE plpgsql
AS $function$
DECLARE
f numeric;
BEGIN
EXECUTE 'SELECT CAST($1 AS numeric(12, ' || scale ||'))' INTO f USING something;
RETURN f;
END;
$function$
;
select somefunction(126.787,2);
somefunction
--------------
126.79
(1 row)
select somefunction(126.787,1);
somefunction
--------------
126.8

Declare type of setof bigint in plpgsql function and assign into from select union

The following code currently works on PostgreSQL 13.3 (via Supabase.io). Both functions get_owned_base_ids and get_bases_editable have return type of setof bigint:
CREATE FUNCTION get_owned_base_ids()
returns setof bigint
stable
language sql
as $$
select id
from bases
where bases.owner_user_id = auth.uid();
$$;
-- CREATE FUNCTION get_bases_editable()
-- returns setof bigint
-- ... similar to get_owned_base_ids()
-- $$;
CREATE FUNCTION xyz (base_id bigint)
returns int
language plpgsql
as $$
BEGIN
IF base_id not in (select get_owned_base_ids() UNION select get_bases_editable()) THEN
-- note: actual function logic is simplified for this question
return 1;
END IF;
return 0;
END;
$$;
Is it possible to define a setof bigint and assign that from the select union? Something like this:
CREATE FUNCTION xyz (base_id bigint)
returns int
language plpgsql
as $$
DECLARE
allowed_base_ids bigint; -- needs to be a setof
BEGIN
select into allowed_base_ids get_owned_base_ids() UNION select get_bases_editable();
IF kv.base_id not in allowed_base_ids THEN
-- note: actual function logic is simplified for this question
return 1;
END IF;
return 0;
END;
$$;
It usually does not make much sense and use much memory of the result set is large, but you can use an array:
DECLARE
allowed_base_ids bigint[];
BEGIN
allowed_base_ids := array(SELECT * FROM get_owned_base_ids()
UNION ALL
SELECT * FROM get_bases_editable());
IF kv.base_id <> ALL (allowed_base_ids) THEN
...
END IF;
END;

How to return rows of query result in PostgreSQL's function?

I've tried following tutorials for many times but failed.
Could someone give me some examples please?
Here is my code, it prompts that "ERROR:invalid type name 'SETOF RECORD'"
create or replace function find() returns SETOF RECORD
as $$
declare A SETOF RECORD;
begin
A=(
select x,y
from .......
)
CASE WHEN EXISTS A
THEN returns query A
ELSE returns query (
select x,y
from ......
)
END;
end;
$$ language plpgsql;
Ways to declare set returning function that I remember at the moment:
--example 1
create or replace function test() returns SETOF RECORD
as $$
begin
RETURN QUERY SELECT * FROM generate_series(1,100);
end;
$$ language plpgsql;
--test output
select * from test() AS a(b integer)
--example 2
create or replace function test2() returns TABLE (b integer)
as $$
begin
RETURN QUERY SELECT * FROM generate_series(1,100);
end;
$$ language plpgsql;
--test output
select * from test2()
--example 3
create or replace function test3() returns SETOF RECORD
as $$
declare
r record;
begin
FOR r IN SELECT * FROM generate_series(1,100) LOOP
RETURN NEXT r;
END LOOP;
end;
$$ language plpgsql;
--test output
select * from test3() AS a(b integer);
--example 4
create or replace function test4() returns setof record
as $$
SELECT * FROM generate_series(1,100)
$$ language sql;
--test output
select * from test4() AS a(b integer);
--example 5
create or replace function test5() returns setof integer
as $$
begin
RETURN QUERY SELECT * FROM generate_series(1,100);
end;
$$ language plpgsql;
--test output
select * from test5()
--example 6
create or replace function test6(OUT b integer, OUT c integer) RETURNS SETOF record
as $$
begin
RETURN QUERY SELECT b.b, b.b+3 AS c FROM generate_series(1,100) AS b(b);
end;
$$ language plpgsql;
--test output
select * from test6()

Table name as variable parameter

I made a function in pgadmin
create or replace function get_source2(a text)
returns integer as
$$
declare
a text;
geom geometry;
begin
select get_source(geom)
from a;
end;
$$
language plpgsql;
I want input a by table name How can I do? I try to like this
select get_source2('postgis.center')
but I get:
ERROR: relation "a" does not exist LINE 2: from a help me
try this:
create or replace function get_source2(a text)
returns integer as
$$
declare
geom geometry;
begin
execute 'select get_source(geom) from '||quote_ident(a) into geom;
return geom;
end;
$$
language plpgsql;

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.