I am using PostgreSQL 8.4 and I want to create a function that returns a query with many rows.
The following function does not work:
create function get_names(varchar) returns setof record AS $$
declare
tname alias for $1;
res setof record;
begin
select * into res from mytable where name = tname;
return res;
end;
$$ LANGUAGE plpgsql;
The type record only allows single row.
How to return an entire query? I want to use functions as query templates.
CREATE OR REPLACE FUNCTION get_names(_tname varchar)
RETURNS TABLE (col_a integer, col_b text) AS
$func$
BEGIN
RETURN QUERY
SELECT t.col_a, t.col_b -- must match RETURNS TABLE
FROM mytable t
WHERE t.name = _tname;
END
$func$ LANGUAGE plpgsql;
Call like this:
SELECT * FROM get_names('name')
Major points:
Use RETURNS TABLE, so you don't have to provide a list of column names with every call.
Use RETURN QUERY, much simpler.
Table-qualify column names to avoid naming conflicts with identically named OUT parameters (including columns declared with RETURNS TABLE).
Use a named variable instead of ALIAS. Simpler, doing the same, and it's the preferred way.
A simple function like this could also be written in LANGUAGE sql:
CREATE OR REPLACE FUNCTION get_names(_tname varchar)
RETURNS TABLE (col_a integer, col_b text) AS
$func$
SELECT t.col_a, t.col_b --, more columns - must match RETURNS above
FROM mytable t
WHERE t.name = $1;
$func$ LANGUAGE sql;
Related
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()?
My application uses multiple schemas to partition tenants across the database to improve performance. I am trying to create a plpgsql function that will give me an arbitrary result set based on the union of all application schemas given a table. Here is what I have so far (inspired by this blog post):
CREATE OR REPLACE FUNCTION app_union(tbl text) RETURNS SETOF RECORD AS $$
DECLARE
schema RECORD;
sql TEXT := '';
BEGIN
FOR schema IN EXECUTE 'SELECT distinct schema FROM tenants' LOOP
sql := sql || format('SELECT * FROM %I.%I %s UNION ALL ', schema.schema, tbl);
END LOOP;
RETURN QUERY EXECUTE left(sql, -11);
END
$$ LANGUAGE plpgsql;
This works great, but has to be called with a row type definition at the end:
select * from app_union('my_table') t(id uuid, name text, ...);
So, how can I call my function without providing a row type?
I know that I can introspect my tables using information_schema.columns, but I'm not sure how to dynamically generate the type declaration without a lot of case statements (columns doesn't report the definition sql the way that e.g., pg_indexes does).
Even if I could dynamically generate the row declaration, it seems I would have to append it to my former function call as dynamic sql anyway, which sort of chicken/eggs the problem of returning a result set of an arbitrary type from a function.
Instead of providing the table as a string, you could provide it as type anyelement to specify the actual type of the returning data, then infer the table's name using pg_typeof. You can also use string_agg rather than a loop to build your sql:
CREATE OR REPLACE FUNCTION app_union(tbl anyelement)
RETURNS setof anyelement AS $$
BEGIN
return query execute string_agg(
distinct format('select * from %I.%I', schema, pg_typeof(tbl)::text),
' union all '
) from tenants;
END
$$ LANGUAGE plpgsql;
select * from app_union(null::my_table);
Simplified example
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 do i create a Procedure that returns a set of rows from a table?
or is it even possible to return a tabular result set with procedure.
I tried adding returns setof students like you do in a function and table(id int) but it doesn't work.
SAMPLE CODE:
CREATE OR REPLACE PROCEDURE getStudents()
LANGUAGE plpgsql
AS $$
BEGIN
SELECT * FROM STUDENTS
COMMIT;
RETURN;
END;
$$;
I can call it but it says query has no destination for result data
Procedures aren't meant to return data, that's what functions are for.
You can use a plain SQL function for this, no need for PL/pgSQL:
CREATE OR REPLACE funct get_students()
returns setof student
LANGUAGE sqö
AS $$
select *
from students;
$$;
Then use it like a table:
select *
from get_students();
There is also no need for a commit.
Try to use function instead of procedure. I usually use this.
You need to create a ctype for fetching the data.
Put whatever columns you have to fetch from STUDENTS table.
Syntax is as follows:
CREATE TYPE students_data_ctype AS
(
column_1 int4,
column_2 varchar(100),
column_3 varchar(500)
)
Then create a funcction :
CREATE
OR
REPLACE
FUNCTION PUBLIC.getStudents
()
RETURNS SETOF students_data_ctype AS $BODY$ DECLARE res
students_data_ctype;
BEGIN
FOR res IN
SELECT
column_1,
column_2,
column_3
FROM
STUDENTS
LOOP RETURN NEXT res;
END LOOP;
END
; $BODY$ LANGUAGE 'plpgsql'
GO
Function call :
Select * FROM getStudents()
Taddaaa! You will get your data.
I want to return a row from a table with this function (I don't know the name of the table, it's random)
CREATE FUNCTION foo( text ) RETURNS setof record AS $$
DECLARE
table_name ALIAS FOR $1;
BEGIN
SELECT * from table_name ;
END
$$ LANGUAGE plpgsql;
and then I want to do this:
select col1,col2 from foo('bar');
Any ideas?
SQL demands to know the return type at call time. And functions require you to define a return type as well. What you are after is not trivial.
If you don't know the return type at call time, you are basically out of luck. You cannot solve the problem with a single function call.
If you know the type at call time, there is an option with polymorphic types and dynamic SQL with EXECUTE:
CREATE OR REPLACE FUNCTION f_data_of_table(_tbl_type anyelement)
RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE
'SELECT * FROM ' || pg_typeof(_tbl_type);
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM f_data_of_table(NULL::my_table_name);
Details in this related answer:
Refactor a PL/pgSQL function to return the output of various SELECT queries
Be wary of SQL injection:
Table name as a PostgreSQL function parameter
Only makes sense if you do more than just SELECT * FROM tbl, or you'd simply use the SQL command.
Aside:
Do not use ALIAS to attach names to parameter values. That's outdated and discouraged. Use named parameters instead.