Not able to call multiple functions from one function in PostgreSQL - database

Here are my two functions,
CREATE OR REPLACE FUNCTION public.insert_stagging()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT into stagging(data)
select data from mytable where data not like 'table%';
END;
$function$;
CREATE OR REPLACE FUNCTION clear_mytable2()
RETURNS TABLE(data1 text) as
$BODY$
BEGIN
RETURN QUERY (select data from mytable2);
END;
$BODY$
LANGUAGE plpgsql;
Not able to call above two functions from another function,
CREATE OR REPLACE FUNCTION func_test()
returns void as
$func$
BEGIN
EXECUTE 'SELECT insert_stagging()';
EXECUTE 'SELECT clear_mytable2()';
END
$func$ LANGUAGE plpgsql;
With the above code, I am only able to get the execution result of 1st function written inside the BEGIN clause, if I change the sequence of the 'SELECT insert_stagging()'; with 'SELECT clear_mytable2()'; then I'll only get the execution result from clear_mytable2() function.
How to execute both the function calls within 1 PostgreSQL function?

When the function returns table, then returns result just to caller function. When the caller function ignores this result, then result is thrown.
If you want to call void function, then you should to use PERFORM. Using EXECUTE in this context is bad idea. This is designed to work with dynamic SQL. If you want to call table function, you have to use RETURN QUERY again:
CREATE OR REPLACE FUNCTION func_test()
RETURNS TABLE(data1 text) AS
$func$
BEGIN
PERFORM insert_stagging();
RETURN QUERY SELECT * FROM clear_mytable2();
END
$func$ LANGUAGE plpgsql;
SELECT * FROM func_test();

Related

Create generate random number function in SQL Server

I'm trying to create the function in SQL Server. In this function I have generated the random number, but function not generated.
Create function [GetRandomNumber]
(
)
RETURNS bigint
as
Begin
Declare #randomNo int
set #randomNo = (select round(rand(checksum(newid()))*(10001)+50000,0) as [GetRandomNumber])
return #randomNo
End
this is generated in following error:
Invalid use of a side-effecting operator 'newid' within a function.
Msg 443, Level 16, State 1, Procedure GetRandomNumber, Line 8
Invalid use of a side-effecting operator 'rand' within a function.
You can. However, it will require a little bit of extra legwork.
First, you need to create a view, like the one below:
create view dbo.sys_NDF
as
select rand() as [ValueRand], newid() as [ValueGUID],
rand(checksum(newid())) as [SeededRand];
go
The trick is that you cannot call these system functions directly from your UDF, however you can query a view that returns their values. You can later expand it with other functions / columns if need be.
As such, your function starts to look like the following:
Create function [GetRandomNumber]()
RETURNS bigint as begin
return (select round(v.SeededRand * 10001 + 50000, 0) from dbo.sys_NDF v);
end;
go
Create SP instead of function. Because some system function are not allowed in user defined function.
CREATE PROCEDURE [GetRandomNumber]
as
Begin
Declare #randomNo int
set #randomNo = (select round(rand(checksum(newid()))*(10001)+50000,0) as [GetRandomNumber])
return #randomNo
End
GO
DECLARE #returnvalue INT
EXEC #returnvalue = GetRandomNumber
SELECT #returnvalue

Merging and returning arrays

I've two functions,
CREATE OR REPLACE FUNCTION function_a(input varchar)
RETURNS setof integer AS $$
BEGIN
RETURN QUERY
SELECT somecolumn FROM some_things WHERE a_column = input;
END;
$$ LANGUAGE PLpgSQL;
CREATE OR REPLACE FUNCTION function_b(inputs varchar[])
RETURNS setof integer AS $$
DECLARE
input varchar;
result integer[];
BEGIN
FOREACH input IN ARRAY inputs LOOP
result := result || ARRAY[function_a(input)];
END LOOP;
END;
$$ LANGUAGE PLpgSQL;
I am running it like,
SELECT function_b(ARRAY['a', 'b']);
The error,
ERROR: query "SELECT result || ARRAY[function_a(input)]" returned more than one row
CONTEXT: PL/pgSQL function function_b(character varying[]) line 7 at assignment
All I want to do is to run a function over an array. I've always used scripting languages like Ruby to do this kind of stuff instead of using SQL, but I'm trying to learn SQL as it is much faster to get results on the db console itself. I wish it wasn't so frustrating.
First, in SQL we mainly write queries. You can do the same with a simple query:
select somecolumn
from some_things
where a_column = any(array['a', 'b']);
If you need a function, it may be an SQL one:
create or replace function sql_function(inputs text[])
returns setof integer language sql as $$
select somecolumn
from some_things
where a_column = any(inputs);
$$;
SQL functions are simpler and usually faster than plpgsql ones.
Back to your function_b() - the array constructor should look like this:
ARRAY(SELECT function_a(input))
Note also that the function does not return anything. Because you aggregate results in an array, you should unnest it to return rows:
CREATE OR REPLACE FUNCTION function_b(inputs varchar[])
RETURNS setof integer AS $$
DECLARE
input varchar;
result integer[];
BEGIN
FOREACH input IN ARRAY inputs LOOP
result := result || ARRAY(SELECT function_a(input));
END LOOP;
RETURN QUERY SELECT unnest(result);
END;
$$ LANGUAGE PLpgSQL;
You don't need the first function at all. I believe you want to return a set of integers from the table for the given input. A function like this is all you need.
CREATE OR REPLACE FUNCTION function_b(inputs varchar[])
RETURNS setof integer AS $$
BEGIN
RETURN QUERY SELECT somecolumn
FROM some_things WHERE a_column = ANY ( inputs );
END;
$$ LANGUAGE PLpgSQL;
Demo

PostgreSQL RETURN NEXT error "returned more than one row"

I have a long drawn out PGPLSQL function :here is a summary
CREATE FUNCTION get_features_by_buffer(
p_buffer GEOMETRY
)
RETURNS SETOF JSON AS $BODY$
DECLARE
v_buffer GEOMETRY;
v_sql TEXT
BEGIN
FOR REC IN EXECUTE $$(
(
SELECT row_to_json(foo_pole) AS json FROM
(
SELECT * FROM pole WHERE $$ || v_sql_where || $$
) AS foo_pole
)
UNION ALL
(
SELECT row_to_json(foo_transformerbank) AS json FROM
(
SELECT * FROM transformerbank WHERE $$ || v_sql_where || $$) AS foo_transformerbank
)
)$$ LOOP
RETURN NEXT REC.json;
END LOOP;
END
$BODY$
LANGUAGE plpgsql;
My function returns RETURNS SETOF JSON and its a bit more complicated than shown here, however I have run the queries that are inside the UNION ALL statement and there is no syntax or other error. It looks weird here because I've been tinkering but I originally tried it by putting the query statement in v_sql and doing RETURN QUERY EXECUTE v_sql, that also gives the same error as the version show here. The error is as follows:
ERROR: query "SELECT get_features_by_buffer(v_buffer)" returned more than one row
CONTEXT: PL/pgSQL function get_features_by_pole_distance(character varying,double precision) line 7 at RETURN NEXT
I have been tinkering with this for a while, not sure what I'm missing here, something to do with the UNION ALL?
As far as I can tell, you don't need the cursor. Just use the generated SQL as the input to return query execute ...:
It's also easier to generate dynamic SQL using the format() function:
CREATE FUNCTION get_features_by_buffer(p_buffer GEOMETRY)
RETURNS SETOF JSON
AS
$BODY$
DECLARE
v_buffer GEOMETRY;
v_sql TEXT
BEGIN
RETURN QUERY EXECUTE
format(
'SELECT row_to_json(foo_pole) AS json
FROM (
SELECT *
FROM pole
WHERE %s
) AS foo_pole
UNION ALL
SELECT row_to_json(foo_transformerbank) AS json
FROM (
SELECT *
FROM transformerbank
WHERE %s
) AS foo_transformerbank', v_sql_where, v_sql_where);
END
$BODY$
LANGUAGE plpgsql;
As that function is declared as returns setof you have to use it like a table:
select *
from get_features_by_buffer(...);

How to return a new value in a trigger from a cte in PostgreSQL

From my previous question in PostgreSQL (9.3), How to check a sequence efficiently for used and unused values in PostgreSQL, I now have a table chart_gap that has a list of all the unused chart numbers from table charts:
CREATE TABLE chart_gap (chart_number integer);
In trying to understand triggers a bit more, I have added the following trigger procedure taken more or less from that question:
CREATE OR REPLACE FUNCTION next_chart() RETURNS trigger AS $BODY$
BEGIN
-- Check for empty chart number
IF NEW.chart_number IS NULL THEN
WITH ins AS (
SELECT chart_number
FROM chart_gap
WHERE pg_try_advisory_xact_lock(chart_number)
LIMIT 1
)
DELETE FROM chart_gap c
USING ins i
WHERE i.chart_number = c.chart_number;
NEW.chart_number := select chart_number from ins; <--WRONG!
END IF;
RETURN NEW;
END;
$BODY$ LANGUAGE plpgsql VOLATILE;
The trigger procedure is tied to the charts file with:
CREATE TRIGGER next_chart
BEFORE INSERT
ON charts
FOR EACH ROW
EXECUTE PROCEDURE next_chart();
What I am attempting to do is replace the empty chart number field in charts whenever a new chart record is added. Can this be done in a trigger? (and what
is the correct syntax??)
In a plpgsql function you can use query RETURNING ... INTO ... as follows:
CREATE FUNCTION next_chart() RETURNS trigger AS $BODY$
BEGIN
-- Check for empty chart number
IF NEW.chart_number IS NULL THEN
WITH ins AS (
SELECT chart_number
FROM chart_gap
WHERE pg_try_advisory_xact_lock(chart_number)
LIMIT 1
)
DELETE FROM chart_gap c
USING ins i
WHERE i.chart_number = c.chart_number
RETURNING i.chart_number INTO NEW.chart_number;
END IF;
RETURN NEW;
END;
$BODY$ LANGUAGE plpgsql VOLATILE;

Using %rowtype when returning in a PostgreSQL function

If I have a function that returns only one row with some columns from a table. Do I need to add %rowtype in the function return declaration?
CREATE OR REPLACE FUNCTION test(int n)
RETURNS tableName%rowtype AS
$BODY$
DECLARE
r tableName%rowtype;
BEGIN
select a,b,c into r from tableName where d=n;
return r;
$BODY$
END;
About %ROWTYPE
The %ROWTYPE construct is only good for portability to other RDBMS. Rarely useful, since PL/pgSQL functions are hardly portable to begin with.
If you are going to use it, it's only meant for variable declaration inside PL/pgSQL function, not to declare the RETURN type, which is part of the outer SQL syntax.
The manual:
(Since every table has an associated composite type of the same name,
it actually does not matter in PostgreSQL whether you write %ROWTYPE
or not. But the form with %ROWTYPE is more portable.)
Answer
This would achieve what you seem to be trying:
CREATE OR REPLACE FUNCTION test_plpgsql(_n int)
RETURNS tbl
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN (SELECT t FROM tbl t where tbl_id = _n); -- selecting whole row
END
$func$;
Call:
SELECT * FROM test_plpgsql(1);
But if it's as simple as that, use a simpler SQL function to begin with:
CREATE OR REPLACE FUNCTION test_sql(_n int)
RETURNS SETOF tbl
LANGUAGE sql AS
$func$
SELECT * FROM tbl WHERE tbl_id = _n;
$func$;
Call:
SELECT * FROM test_sql(1);
Your original code example had too many issues. Search for more plpgsql examples to get a grasp on basic syntax.

Resources