I want to pass the result of a subselect to a function
Say I've got an existing function that returns boolean
function report_can_edit which takes a report row type. I want to call this from another function which is passed an id for a report
( just a silly example to illustrate what I'm trying to do )
create or replace function report_can_edit(report report) returns boolean as $$
select true; -- Imagine this does some complicated stuff
$$ language sql stable;
create or replace function task_edit(task_report_id int) returns boolean as $$
select report_can_edit((select * from report where id = task_report_id))
$$ language sql stable;
This gives
ERROR: subquery must return only one column
Do I have to switch to plpgsql and select into a decared row type first? or is there a way to do this with an sql type function?
Try:
create or replace function task_edit(task_report_id int)
returns boolean as $$
select report_can_edit((select report from report where id = task_report_id))
$$ language sql stable;
Related
Im new to PostgreSQL v.13 and want to create a Stored Procedure that will receive an integer value and then return the results from a table.
I have this:
create or replace procedure public.sp_message_template_get(
templateid int
) language plpgsql AS $$
begin
-- subtracting the amount from the sender's account
select * from public.message_template;
--commit;
end;
$$
Then I try to call it by using:
call public.sp_message_template_get(1);
And I get the following error:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function sp_message_template_get(integer) line 6 at SQL statement
SQL state: 42601
Any clue?
Thanks
Create a sql function.
create or replace function message_template_get(templateid integer)
returns setof message_template language sql as
$$
-- subtracting the amount from the sender's account (sql query)
select * from public.message_template;
$$;
If there is a reason to use plpgsql language then use return query.
create or replace function message_template_get(templateid integer)
returns setof message_template language plpgsql as
$$
begin
-- subtracting the amount from the sender's account
return query select * from public.message_template;
-- maybe other statements here
end;
$$;
Please note that commit is not necessary and illegal in a function body. More than one return query or return next statements are possible.
Based on this question I would like to know if it is possible to return different table data based on an ID passed to the function.
Something like (pseudocode):
CREATE FUNCTION schemaB.testFunc(p_id INT, select_param INT)
RETURNS setof schemaZ.Table_1
AS
$$
CASE
WHEN select_param = 1 THEN SELECT * FROM schemaZ.Table_1 WHERE id = p_id
WHEN select_param = 2 THEN SELECT * FROM schemaZ.Table_2 WHERE id = p_id
END;
$$
language sql;
Table_1 and Table_2 share no same columns and that invalidates the above RETURNS clause.
This is generally impossible with SQL functions. Even with a polymorphic return type, the actual return type must be determined at call time. But all statements in an SQL function are planned before the function is executed. So you'd always end up with an error message for one of the SELECT statements returning data that doesn't fit the return type.
The same can be done with dynamic SQL in a PL/pgSQL function - with some trickery:
CREATE OR REPLACE FUNCTION f_demo(_tabletype anyelement, _id int)
RETURNS SETOF anyelement LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE
format('SELECT * FROM %s WHERE id = $1', pg_typeof(_tabletype))
USING _id;
END
$func$;
Call (important!):
SELECT * FROM f_demo(null::schemaZ.Table_1, 1);
The "trick" is to cast a null value to the desired table type, thereby defining the return type and choosing from which table to select. Detailed explanation:
Refactor a PL/pgSQL function to return the output of various SELECT queries
Take this as proof of concept. Typically, there are better (safer, less confusing, more performant) solutions ...
Related:
Difference between language sql and language plpgsql in PostgreSQL functions
I am new to postgreSQL. I am trying to convert SQL store procedure into postgreSQL store function. I tried Like below store function created successfully but when I am trying to execute I am getting following error message. I am not getting where I need to replace PERFORM.
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
SQL Store Procedure
CREATE PROCEDURE [dbo].[roomType]
#intInstId int
AS
BEGIN
SELECT
nClientRoomTypeID,sClientRTDesc,sClientRTName,sClientRTCode
FROM
ClientRoomType
WHERE
ClientRoomType.nInstID=#intInstId
ORDER BY
ClientRoomType.sClientRTCode
END
GO
PostgreSQl Store Function
CREATE OR REPLACE FUNCTION roomType (int_inst_id int)
RETURNS VOID
AS $$
BEGIN
SELECT
nclient_room_type_id,sclient_rt_desc,sclient_rt_name,sclient_rt_code
FROM
clientroomtype
WHERE
clientroomtype.ninst_id=int_inst_id
ORDER BY
clientroomtype.sclient_rt_code;
END;
$$ LANGUAGE plpgsql;
I assume you're attempting to return the query result with your function. You actually do not need plpgsql language for this, but in case you need it for somthing else, use this syntax:
CREATE OR REPLACE FUNCTION roomType (int_inst_id int)
RETURNS TABLE (res_nclient_room_type_id INT,res_sclient_rt_desc TEXT,res_sclient_rt_name TEXT, res_sclient_rt_code TEXT)
AS $$
BEGIN
RETURN QUERY
SELECT
nclient_room_type_id,sclient_rt_desc,sclient_rt_name,sclient_rt_code
FROM
clientroomtype
WHERE
clientroomtype.ninst_id=int_inst_id
ORDER BY
clientroomtype.sclient_rt_code;
END;
$$ LANGUAGE plpgsql;
How to use it?
SELECT * FROM roomType(1);
Since I do not have your data, I cannot test it. But it follows this principle:
CREATE OR REPLACE FUNCTION f ()
RETURNS TABLE (b boolean, x int)
AS $$
BEGIN
RETURN QUERY SELECT TRUE, 1;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM f();
b | x
---+---
t | 1
(1 Zeile)
I have an error, but I don't know what the problem is.
I want execute a function and return a value from a column filled in by the column default, a sequence - the equivalent of currval(sequence).
I use:
PostgreSQL 9.0
pgAdmin III
CREATE OR REPLACE FUNCTION name_function(in param_1 character varying
, out param_2 bigint)
AS
$$
BEGIN
INSERT INTO table (collumn_seq,param_1) VALUES (DEFAULT,param_1)
returning collumn_seq;
--where:collumn_seq reference a collumn serial..
END;
$$
LANGUAGE plpgsql VOLATILE;
I can create the function without error but when trying to execute, the following error is returned:
SELECT name_function('GHGHGH');
ERROR: The query has no destination for result data
It would work like this:
CREATE OR REPLACE FUNCTION name_function(param_1 varchar
, OUT param_2 bigint)
LANGUAGE plpgsql AS
$func$
BEGIN
INSERT INTO table (collumn_seq, param_1) -- "param_1" also the column name?
VALUES (DEFAULT, param_1)
RETURNING collumn_seq
INTO param2;
END
$func$;
Normally, you would add a RETURN statement, but with OUT parameters, this is optional.
Refer to the manual for more details:
Returning from a function
Executing a Query with a Single-row Result
The simple case can be covered with a plain SQL function.
And you can omit the target column that shall get its DEFAULT value.
And you can just as well use a RETURNS clause in this case:
CREATE OR REPLACE FUNCTION name_function(param_1 varchar)
RETURNS bigint
LANGUAGE sql AS
$func$
INSERT INTO table (param_1) -- "param_1" also the column name?
VALUES (param_1)
RETURNING collumn_seq;
$func$;
I have spent good amount of time trying to figure it out and I haven't been able to resolve it. So, I need your help please.
I am trying to write a PL/pgSQL function that returns multiple rows. The function I wrote is shown below. But it is not working.
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS SETOF RECORD
AS
$$
DECLARE result_record keyMetrics;
BEGIN
return QUERY SELECT department_id into result_record.visits
from fact_department_daily
where report_date='2013-06-07';
--return result_record;
END
$$ LANGUAGE plpgsql;
SELECT * FROM get_object_fields;
It is returning this error:
ERROR: RETURN cannot have a parameter in function returning set;
use RETURN NEXT at or near "QUERY"
After fixing the bugs #Pavel pointed out, also define your return type properly, or you have to provide a column definition list with every call.
This call:
SELECT * FROM get_object_fields()
... assumes that Postgres knows how to expand *. Since you are returning anonymous records, you get an exception:
ERROR: a column definition list is required for functions returning "record"
One way (of several) to fix this is with RETURNS TABLE (Postgres 8.4+):
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS TABLE (department_id int) AS
$func$
BEGIN
RETURN QUERY
SELECT department_id
FROM fact_department_daily
WHERE report_date = '2013-06-07';
END
$func$ LANGUAGE plpgsql;
Works for SQL functions just the same.
Related:
PostgreSQL: ERROR: 42601: a column definition list is required for functions returning "record"
I see more bugs:
first, a SET RETURNING FUNCTIONS call has following syntax
SELECT * FROM get_object_fields()
second - RETURN QUERY forwards query result to output directly. You cannot store this result to variable - it is not possible ever in PostgreSQL now.
BEGIN
RETURN QUERY SELECT ....; -- result is forwarded to output directly
RETURN; -- there will not be any next result, finish execution
END;
third - these simple functions is better to implement in SQL languages
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS SETOF RECORD AS $$
SELECT department_id WHERE ...
$$ LANGUAGE sql STABLE;
Here's one way
drop function if exists get_test_type();
drop type if exists test_comp;
drop type if exists test_type;
drop type if exists test_person;
create type test_type as (
foo int,
bar int
);
create type test_person as (
first_name text,
last_name text
);
create type test_comp as
(
prop_a test_type[],
prop_b test_person[]
);
create or replace function get_test_type()
returns test_comp
as $$
declare
a test_type[];
b test_person[];
x test_comp;
begin
a := array(
select row (m.message_id, m.message_id)
from message m
);
-- alternative 'strongly typed'
b := array[
row('Bob', 'Jones')::test_person,
row('Mike', 'Reid')::test_person
]::test_person[];
-- alternative 'loosely typed'
b := array[
row('Bob', 'Jones'),
row('Mike', 'Reid')
];
-- using a select
b := array (
select row ('Jake', 'Scott')
union all
select row ('Suraksha', 'Setty')
);
x := row(a, b);
return x;
end;
$$
language 'plpgsql' stable;
select * from get_test_type();
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS table (department_id integer)
AS
$$
DECLARE result_record keyMetrics;
BEGIN
return QUERY
SELECT department_id
from fact_department_daily
where report_date='2013-06-07';
--return result_record;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM get_object_fields()