How do I pass in the array output of SQL query into a PostgreSQL (PL/pgSQL) function? - sql

I am able to do the following in SQL where an "array" of user_ids are passed into the where clause of a SQL query.
select * from users where id in (select user_id from profiles);
I would like to do the same thing but pass the "array" into a PostgreSQL (PL/pgSQL) function as shown below. How do I declare the function and work with the "array" within the function?
select * from users_function(select user_id from profiles);
CREATE OR REPLACE FUNCTION users_function(....)
RETURNS void AS
$BODY$
....

Declare an array datatype [] in the function then use the aggregate function array_agg to transform the select statement into an array.
CREATE OR REPLACE FUNCTION users_function(myints integer[])
$$
BEGIN
-- you need to find the bounds with array_lower and array_upper
FOR i in array_lower(myints, 1) .. array_upper(myints, 1) LOOP
Raise Notice '%', myints[i]::integer;
END LOOP;
END;
$$
select * from users_function(array_agg((select user_id from profiles)));

I could not get the nate c's array_agg approach as I described above. This is an option:
select * from test_array('{1,2}');
CREATE OR REPLACE FUNCTION test_array(user_ids integer[])
RETURNS void AS
$$
declare
begin
FOR i in array_lower(user_ids, 1) .. array_upper(user_ids, 1) LOOP
RAISE NOTICE '%', user_ids[i]::integer;
END LOOP;
end
$$
LANGUAGE plpgsql;

Related

How do I return a table from a function with a bespoke column name?

This function works:
CREATE OR REPLACE FUNCTION public.a()
RETURNS TABLE(a text)
LANGUAGE plpgsql
AS $function$
BEGIN
return query execute
'select a from ztable';
END;
$function$;
But when I try to add some text to the column name:
CREATE OR REPLACE FUNCTION public.a(prefix text)
RETURNS TABLE(a text)
LANGUAGE plpgsql
AS $function$
BEGIN
return query execute
'select a as $1_a from ztable' using prefix;
END;
$function$;
This just fails as a syntax error on $1.
Or:
CREATE OR REPLACE FUNCTION public.a(prefix text)
RETURNS TABLE(a text)
LANGUAGE plpgsql
AS $function$
BEGIN
return query execute
'select a as '||prefix||'_a from ztable';
END;
$function$;
select * from a('some prefix') doesn't work.
Is there some other syntax that does the job?
That's simply not possible. SQL does not allow dynamic column names.
You must assign a column alias with the call. Like:
SELECT a AS prefix_a FROM public.a();
Or in a column definition list directly attached to the function:
SELECT * FROM public.a() AS f(prefix_a);
Or, while dealing with a single output column, even just:
SELECT * FROM public.a() AS prefix_a;
See:
RETURNING rows using unnest()?

Postgresql, function returns query by calling another function

Postgresql 12. Want a function to return query by calling another function but don't know how to call.
create or replace function getFromA()
returns table(_id bigint, _name varchar) as $$
begin
RETURN QUERY SELECT id, name from groups;
end; $$ language plpgsql;
create or replace function getFromB()
returns table(_id bigint, _name varchar) as $$
begin
return query select getFromA();
end; $$ language plpgsql;
select getFromB();
gets error:
SQL Error [42804]: ERROR: structure of query does not match function result type
Detail: Returned type record does not match expected type bigint in column 1.
Where: PL/pgSQL function getfromb() line 3 at RETURN QUERY
How to fix this?
The problem is in getFromB():
return query select getFromA();
Unlike some other databases, Postgres allows set-returning functions directly in the select clause. This works, but can be tricky: this returns a set, hence not the expected structure.
You would need to select ... from getFromA() instead: this way it returns the proper data structure.
create or replace function getFromB()
returns table(_id bigint, _name varchar) as $$
begin
return query select * from getFromA();
end; $$ language plpgsql;
Demo on DB Fiddle

How to insert rows to table in a loop

I have the following plpgsql function in PostgreSQL:
CREATE OR REPLACE FUNCTION func1()
RETURNS SETOF type_a AS
$BODY$
declare
param text;
sqls varchar;
row type_a;
begin
code.....
sqls='select * from func3(' || param || ') ';
for row in execute sqls LOOP
return next row;
END LOOP;
end if;
return;
end
$BODY$
LANGUAGE plpgsql VOLATILE
I want to add an insert statment into the loop, so that the loop will work as it is now but also all rows will be saved in a table.
for row in execute sqls LOOP
INSERT INTO TABLE new_tab(id, name)
return next row;
the thing is that I don't know how to do that... the insert statment normaly has syntax of:
INSERT INTO new_tab(id, name)
SELECT x.id, x.name
FROM y
but this syntax doesn't fit here. There is no query to select rows from.... the rows are in the loop.
Basic insert with values looks like this:
INSERT INTO table_name (column1,column2,column3,...)
VALUES (value1,value2,value3,...);
Based on the additional comments you need to use cursor instead of execute sqls.
No need for a loop, you can use insert .. select ... returning in dynamic SQL just as well:
create or replace function func1()
returns table (id integer, name text)
as
$$
declare
param text;
begin
param := ... ;
return query execute
'insert into new_tab (id, name)
select id, name
from func3($1)
returning *'
using param;
end;
$$
language plpgsql;
Note that I used a parameter placeholder and the USING clause instead of concatenating the parameter into the query - much more robust.

How to declare returning type for function, returning unnamed table

I want to create a function, that returns a table with 2 columns:
i integer -- or bigint?
arr integer[] -- array of integer
What should I write instead of ??? in this function:
CREATE OR REPLACE FUNCTION test()
RETURNS ???
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY (
SELECT i, (ARRAY[11,22,33])[i]
FROM generate_series(
1,
array_upper(ARRAY[11,22,33],1)
) i
);
END;
$$;
see the answer for this question,
example of function return table:
CREATE OR REPLACE FUNCTION foo(a int)
RETURNS TABLE(b int, c int) AS $$
BEGIN
RETURN QUERY SELECT i, i+1 FROM generate_series(1, a) g(i);
END;
$$ LANGUAGE plpgsql;
You don't need plpgsql for this. A simple SQL function would do the job:
CREATE OR REPLACE FUNCTION test(_arr anyarray)
RETURNS TABLE (idx int, elem anyelement)
$func$
SELECT i, _arr[i] FROM generate_subscripts(_arr, 1) i
$func$ LANGUAGE sql AS
Call:
SELECT * FROM test(ARRAY[11,22,33]::int[]); -- Cast to declare type for literals
The polymorphic parameter anyarray works for arrays of any base type.
How to write a function that returns text or integer values?
Use generate_subscripts() to simplify the task.
More about returning from a function:
How to return result of a SELECT inside a function in PostgreSQL?
Postgres 9.4
There is a shiny new trick in upcoming Postgres 9.4: WITH ORDINALITY. Details in this related answer:
PostgreSQL unnest() with element number
Simplify to (no additional function):
SELECT * FROM unnest(ARRAY[11,22,33]::int[]) WITH ORDINALITY AS x (elem, idx)

Postgres function with list argument and in clause

How to create a function which takes as argument integer[] parameter and executing query with IN clause with this parameter in loop.
In loop I want execute next select and result of this query I would like return.
Something like that:
CREATE OR REPLACE FUNCTION function_which_i_want(my_argument integer[]) RETURNS my_schema_and_table[] AS
$BODY$
DECLARE
result my_schema_and_table[];
BEGIN
FOR l IN SELECT * FROM table2 WHERE id in (my_argument) LOOP
SELECT * FROM my_schema_and_table;
END LOOP;
END;
...
I want to get union of each select in loop. one huge joined result.
Is this possible? Please help.
PL/pgSQL function
It could look like this:
CREATE OR REPLACE FUNCTION func1(_arr integer[])
RETURNS SETOF my_schema_and_table
LANGUAGE plpgsql AS
$func$
DECLARE
l record;
BEGIN
FOR l IN
SELECT *
FROM lookup_table
WHERE some_id = ANY(_arr)
LOOP
RETURN QUERY
SELECT *
FROM my_schema_and_table
WHERE link_id = l.link_id;
END LOOP;
END
$func$;
Assuming you actually want a SET of rows from your my_schema_and_table, not an array? To return the result of SELECT * FROM my_schema_and_table, declare the function as RETURNS SETOF my_schema_and_table
Rewrite the IN construct to = ANY(_arr). That's the way to use an array parameter directly. Logically equivalent.
Or use unnest() and join to the resulting table like #Clodoaldo demonstrates. That can be faster with long arrays.
Simplify to plain SQL function
This simple SQL function does the same:
CREATE OR REPLACE FUNCTION func2(_arr integer[])
RETURNS SETOF my_schema_and_table
LANGUAGE sql AS
$func$
SELECT t.*
FROM (SELECT unnest($1) AS some_id) x
JOIN lookup_table l USING (some_id)
JOIN my_schema_and_table t USING (link_id);
$func$
Assuming both tables have link_id.
Call:
SELECT * FROM func2('{21,31}'::int[]);
CREATE OR REPLACE FUNCTION function_which_i_want(my_argument integer[])
RETURNS my_schema_and_table[] AS
$BODY$
DECLARE
result my_schema_and_table[];
BEGIN
for l in
select t.*
from
table2 t
inner join
unnest(my_argument) m(id) on m.id = t.id
loop
SELECT * FROM my_schema_and_table;
END LOOP;
END;