PostgreSQL How to use a function in select statement - postgresql

I have a SQL table having two columns 'Product_Type' and 'Product_SubType'. A product has a unique name defined based on combining the type and subtype. But the table does not defined the product name. When I query the table, I would like to have the product name presented.
So I am trying to create a function containing cases of different types and output a product name in a select statement:
create function getNameByTypes( MainType varchar, SubType varchar)
return varchar as $$
declare TypeName varchar;
begin
TypeName = case MainType
when 'type_I' then
case SubType
when 'sub_type_1' then 'TypeName1'
when 'sub_type_2' then 'TypeName2'
end
else 'unknown_type' end;
return TypeName;
end;
$$
And use the function like:
select *, getNameByParams(Product_Type, Product_SubType) as TypeName
where id = 'ABC'
I am getting an error SQL Error [42601]: ERROR: syntax error at or near "return". My question is: what is the correct way to create the function and use the function to query?

you must change return varchar clause for returns varchar in functions definition, and missing the function language, see the following:
create function getNameByTypes( MainType varchar, SubType varchar)
returns varchar as $$
declare TypeName varchar;
begin
TypeName = case MainType
when 'type_I' then
case SubType
when 'sub_type_1' then 'TypeName1'
when 'sub_type_2' then 'TypeName2'
end
else 'unknown_type' end;
return TypeName;
end;
$$
language plpgsql;
And use with your table in from clause:
select *, getNameByParams(Product_Type, Product_SubType) as TypeName from your_table
where id = 'ABC'

Related

plpgSQL return record type from FUNCTION

I'm trying to use a plgSQL witch return me a record type :
CREATE FUNCTION actu(id INTEGER) RETURNS RECORD AS $$
DECLARE
ret RECORD;
BEGIN
SELECT id_photo, id_user, lien, titre
FROM photo
WHERE id_user IN (
SELECT id_userabo
FROM abo
WHERE id_user = id )
ORDER BY date_publi DESC LIMIT 10;
RETURN ret;
END;$$
LANGUAGE plpgsql;
When I'm trying to use it with :
SELECT * FROM actu(4)
AS (id_photo Integer, id_photo Integer, lien Varchar, titre Varchar);
pgAdmin4 send me error :
ERROR: ERROR: the request has no destination for the resulting data
HINT: If you want to cancel the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgsql function fil_actu(integer), line 5 with SQL statement
The immediate error is, that the result of a select statement needs to be stored somewhere (that's what the error says). You would need to use select .. into ret from ... to store the result, but that wouldn't work as a variable of type record can only store one row from a result.
You apparently want to return more than just one row, so you need to define the function as returns table() and then use return query in PL/pgSQL to return the result of a query. But for a simple function encapsulating a SELECT query, a language sql function is more efficient.
CREATE FUNCTION actu(id INTEGER)
-- adjust the data types for the returned columns!
RETURNS table (id_photo int, id_user int, lien text, titre text)
AS $$
SELECT id_photo, id_user, lien, titre
FROM photo
WHERE id_user IN (SELECT id_userabo
FROM abo
WHERE id_user = id )
ORDER BY date_publi DESC
LIMIT 10;
$$
LANGUAGE sql;
You can use that function like this:
select *
from actu(42);
You may define those types inside the function, but with different names for the types. The return type can be a TABLE type and use RETURN QUERY to return the results.
CREATE FUNCTION actu(id INTEGER) RETURNS TABLE
(typ_id_photo Integer, typ_id_user Integer, typ_lien Varchar, typ_titre Varchar)
AS $$
BEGIN
RETURN QUERY
SELECT id_photo, id_user, lien, titre
FROM photo p
WHERE id_user IN (SELECT id_userabo
FROM abo
WHERE id_user = id )
ORDER BY date_publi DESC
LIMIT 10;
END;$$
LANGUAGE plpgsql;

Rename the column name of a stored function

I've got a postgresql stored procedure, which is returning an integer.
When I call that function, the result is returned with the function name as column name.
For example the name of the function is: "add-person". The column name, when invoking the function, is "add-person".
Is there a way to make the database return the integer with a self-choosen column name? For example "id"?
I think it is pretty easy, but I currently miss the forests for the trees..
Edit:
What i'd missed to tell, is that the return value is a variable, like so:
CREATE OR REPLACE FUNCTION "scheme"."add-person"(arggivenname character varying, argfamilyname character varying) RETURNS integer AS
$BODY$
DECLARE
varResponse integer;
BEGIN
-- Operations before
INSERT INTO "scheme"."table"
(
given_name,
family_name
)
VALUES
(
arggivenname,
argfamilyname
)
RETURNING
"id"
INTO
varResponse;
-- Operations after
RETURN varResponse;
END;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
You can us the AS statement for that. That means:
Select add-person() AS yourcolumnname
To have a named column from a function it is necessary to create a type and return that type from the function
create type mytype as (mycolumn integer);
create or replace function ri()
returns mytype as $$
select 1;
$$ language sql;
select * from ri();
mycolumn
----------
1
Edit
Or much simpler without the type creation as in #pozs comment:
create or replace function ri(out mycolumn integer)
as $$
select 1;
$$ language sql;

How to return multiple rows from PL/pgSQL function?

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()

Unexpected behaviour for custom type returned from a function

I have created a custom type
CREATE TYPE rc_test_type AS (a1 bigint);
and a function
CREATE OR REPLACE FUNCTION public.rc_test_type_function(test_table character varying, dummy integer)
RETURNS rc_test_type AS
$BODY$
DECLARE
ret rc_test_type;
query text;
BEGIN
query := 'SELECT count(*) from ' || test_table ;
EXECUTE query into ret.a1;
RETURN ret;
END $BODY$
LANGUAGE plpgsql VOLATILE
If I run
SELECT * FROM rc_test_type_function('some_table', 1);
I get
"a1"
1389
So far so good.
If I run
SELECT p FROM (SELECT rc_test_type_function('some_table', s.step) AS p
FROM some_other_table s) foo;
I get
"p"
"(1389)"
"(1389)"
since 'some_other_table' has just two records. Fine.
But then if I try
SELECT p.a1 FROM (select rc_test_type_function('some_table', s.step) AS p
FROM some_other_table s) foo;
I get the error
missing FROM-clause entry in subquery for table »p«
which I find strange since the subquery has not changed.
Two questions:
Can anyone explain what's going on?
How do I extract the field value a1 from the returned array?
Use parentheses around the composite type:
SELECT (p).a1
FROM (SELECT rc_test_type_function('some_table', s.step) AS p
FROM some_other_table s
) foo;
Even though your type has just a single column is still a composite type - with its own column name. Doesn't make a lot of sense, but that's how you built it.
(You might want to just use a simple type or maybe a DOMAIN instead.)
Quoting the manual here:
(compositecol).somefield
(mytable.compositecol).somefield
The parentheses are required here to show that compositecol is a column name not a
a table name, or that mytable is a table name not a schema name in the second case.
Proper function
Omitting the part with the composite type, your function would be safer, simpler and faster this way:
CREATE OR REPLACE FUNCTION foo(test_table varchar, dummy int, OUT p bigint)
AS
$func$
BEGIN
EXECUTE format('SELECT count(*) from %I', test_table) -- !avoid SQLi!
INTO p;
END
$func$ LANGUAGE plpgsql;
Avoid SQL injection with dynamic SQL!
An OUT parameter simplifies the syntax in this case. You don't need a DECLARE clause at all, and no RETURN either
Even better
CREATE OR REPLACE FUNCTION foo(test_table regclass, dummy int, OUT p bigint)
AS
$func$
BEGIN
EXECUTE 'SELECT count(*) from ' || test_table
INTO p;
END
$func$ LANGUAGE plpgsql;
By using the object identifier regclass this would also work with schema-qualified table names. And SQLi is not possible to begin with. The function would fail immediately if the table name is illegal and it is quoted automatically when converted to text automatically.

pgsql return table ERROR: column reference is ambiguous

I keep getting this ERROR: column reference "person" is ambiguous.
It is required of me to return a TABLE (person integer). It works fine when I use SETOF integer but in this instance it doesn't work. My other function recurse() returns a set of integers perfectly well.
CREATE OR REPLACE FUNCTION try(_group text) RETURNS TABLE (person integer) AS $$
DECLARE
_init_id integer;
_record integer;
BEGIN
SELECT id INTO _init_id FROM egroups WHERE name = _group;
FOR _record in SELECT person FROM egroupdata WHERE egroup IN (SELECT recurse(_init_id))
LOOP
RETURN NEXT;
END LOOP;
END;
$$ language plpgsql stable;
Ambiguous column references are due to there being more than one column available of the same name. In this case I guess it's a quirk of returning a table. Try changing the query to:
SELECT egroupdata.person FROM egroupdata WHERE egroup IN (SELECT recurse(_init_id))
This will disambiguate the column reference.