How to ARRAY values from a SELECT query in POSTGRES - postgresql

I am trying to structure an array within my postgres call by pulling 3 values (all SMALLINT's) from a table and turning them into an array so that I can use them in the rest of the call like so code_list[0].
Currently, I have only created this part of the function so that I can ensure that I am structuring it correctly before I proceed. However, I receive this error error: subquery must return only one column which makes me thing that it assume that I am trying to return a TABLE. I can't save a table in into one value as far as I am aware so I am trying to create an array instead.
Am I creating an ARRAY properly? Is there a way to transform this into JSONB if that would be a better strategy?
CREATE OR REPLACE FUNCTION "RetrieveCodeValues" (
"#code" VARCHAR(50)
)
RETURNS SMALLINT[] AS
$func$
BEGIN
SELECT ARRAY (
SELECT c."big", c."mid", c."small"
FROM "codes" AS c
WHERE "code" = "#code"
) AS code_list;
RETURN code_list;
END;
$func$ LANGUAGE PLPGSQL;

Use the array constructor:
DECLARE res integer[];
BEGIN
SELECT ARRAY[c.big, c.mid, c.small] INTO res
FROM ...
RETURN res;
END;

Related

PostgreSQL - Declaring a variable that can store multiple values

I need a way to declare a variable that can store multiple values. My first attempt was to declare a variable using the TABLE type:
DECLARE __id TABLE(results_id integer);
However this didnt go as planned, giving me type-declaration errors. My next attempt was to make an integer[] type
DECLARE __id integer[];
but it ended up giving me an error of that values needs to be inserted using curly braces whenever i attempted to insert them with a select function.
SELECT p.id FROM files.main p
WHERE p.reference = __reference
AND p.platform = __platform_id
INTO __id;
I wonder if there is any way to solve this problem?
If you have a table name t you can declare a variable of the type t
create or replace function tf1() returns int as
$BODY$
DECLARE
var public.t;
BEGIN
select * from public.t into var limit 1;
return var.id;
END;
$BODY$
LANGUAGE plpgsql ;
select * from tf1();
i need an array to use them afterwards, where every result will be run in a function and the results will be inserted into a table object of same type – Crated
While you can do this with arrays and variables, it's simpler and faster to do it in a single query using an insert into select.
INSERT INTO some_other_table (some_column)
SELECT your_function(p.id)
FROM files.main p
WHERE p.reference = __reference
AND p.platform = __platform_id
This will run each matching p.id through your_function and insert the results as some_column in some_other_table.

Query on Return Statement - PostgreSQL

I have this question, I was doing some migration from SQL Server to PostgreSQL 12.
The scenario, I am trying to accomplish:
The function should have a RETURN Statement, be it with SETOF 'tableType' or RETURN TABLE ( some number of columns )
The body starts with a count of records, if there is no record found based on input parameters, then simply Return Zero (0), else, return the entire set of record defined in the RETURN Statement.
The Equivalent part in SQL Server or Oracle is: They can just put a SELECT Statement inside a Procedure to accomplish this. But, its a kind of difficult in case of PostgreSQL.
Any suggestion, please.
What I could accomplish still now - If no record found, it will simply return NULL, may be using PERFORM, or may be selecting NULL as column name for the returning tableType columns.
I hope I am clear !
What I want is something like -
============================================================
CREATE OR REPLACE FUNCTION public.get_some_data(
id integer)
RETURNS TABLE ( id_1 integer, name character varying )
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
p_id alias for $1;
v_cnt integer:=0;
BEGIN
SELECT COUNT(1) FROM public.exampleTable e
WHERE id::integer = e.id::integer;
IF v_cnt= 0 THEN
SELECT 0;
ELSE
SELECT
a.id, a.name
public.exampleTable a
where a.id = p_id;
END;
$BODY$;
If you just want to return a set of a single table, using returns setof some_table is indeed the easiest way. The most basic SQL function to do that would be:
create function get_data()
returns setof some_table
as
$$
select *
from some_table;
$$
language sql;
PL/pgSQL isn't really necessary to put a SELECT statement into a function, but if you need to do other things, you need to use RETURN QUERY in a PL/pgSQL function:
create function get_data()
returns setof some_table
as
$$
begin
return query
select *
from some_table;
end;
$$
language plpgsql;
A function as exactly one return type. You can't have a function that sometimes returns an integer and sometimes returns thousands of rows with a dozen columns.
The only thing you could do, if you insist on returning something is something like this:
create function get_data()
returns setof some_table
as
$$
begin
return query
select *
from some_table;
if not found then
return query
select (null::some_table).*;
end if;
end;
$$
language plpgsql;
But I would consider the above an extremely ugly and confusing (not to say stupid) solution. I certainly wouldn't let that pass through a code review.
The caller of the function can test if something was returned in the same way I implemented that ugly hack: check the found variable after using the function.
One more hack to get as close as possible to what you want. But I will repeat what others have told you: You cannot do what you want directly. Just because MS SQL Server lets you get away poor coding does not mean Postgres is obligated to do so. As the link by #a_horse_with_no_name implies converting code is easy, once you migrate how you think about the problem in the first place. The closest you can get is return a tuple with a 0 id. The following is one way.
create or replace function public.get_some_data(
p_id integer)
returns table ( id integer, name character varying )
language plpgsql
as $$
declare
v_at_least_one boolean = false;
v_exp_rec record;
begin
for v_exp_rec in
select a.id, a.name
from public.exampletable a
where a.id = p_id
union all
select 0,null
loop
if v_exp_rec.id::integer > 0
or (v_exp_rec.id::integer = 0 and not v_at_least_one)
then
id = v_exp_rec.id;
name = v_exp_rec.name;
return next;
v_at_least_one = true;
end if;
end loop ;
return;
end
$$;
But that is still just a hack and assumes there in not valid row with id=0. A much better approach would by for the calling routing to check what the function returns (it has to do that in one way or another anyway) and let the function just return the data found instead of making up data. That is that mindset shift. Doing that you can reduce this function to a simple select statement:
create or replace function public.get_some_data2(
p_id integer)
returns table ( id integer, name character varying )
language sql strict
as $$
select a.id, a.name
from public.exampletable a
where a.id = p_id;
$$;
Or one of the other solutions offered.

PostgreSQL 9.5: Return dynamic columns list

I have the following function to return the records from different tables based on given condition to
the function.
Function: udf_dynamic_returns
CREATE OR REPLACE FUNCTION udf_dynamic_returns
(
condition_name varchar(10)
)
RETURNS ????
AS
$BODY$
BEGIN
IF condition_name = 'A'
THEN
SELECT col1,col2,col3
FROM Test
ELSE IF condition_name = 'B'
THEN
SELECT col21col22,col23,col24,col25
FROM Kon
ELSE IF condition_name = 'C'
THEN
SELECT col31,col32
FROM Mat
ELSE IF condition_name = 'D'
THEN
SELECT col4,col5,col6,col7,col8,col9
FROM Test
......
......
END IF
END;
$BODY$
LANGUAGE PLPGSQL;
Note: I have many conditions to check within the function.
By searching i came to know about Polymorphic Types. But i don't think it's really a good idea to create so many Composite types.
You must use RETURNS RECORD or RETURNS SETOF RECORD.
But then the caller must specify the column-list when invoking the function. PostgreSQL's planner does not currently know how to execute the function to determine its result-columns-list, then parse and plan the rest of the query once it knows the result-column-list for the function.
See the PostgreSQL documentation chapter on returning records.

Stored function with temporary table in postgresql

Im new to writing stored functions in postgresql and in general . I'm trying to write onw with an input parameter and return a set of results stored in a temporary table.
I do the following in my function .
1) Get a list of all the consumers and store their id's stored in a temp table.
2) Iterate over a particular table and retrieve values corresponding to each value from the above list and store in a temp table.
3)Return the temp table.
Here's the function that I've tried to write by myself ,
create or replace function getPumps(status varchar) returns setof record as $$ (setof record?)
DECLARE
cons_id integer[];
i integer;
temp table tmp_table;--Point B
BEGIN
select consumer_id into cons_id from db_consumer_pump_details;
FOR i in select * from cons_id LOOP
select objectid,pump_id,pump_serial_id,repdate,pumpmake,db_consumer_pump_details.status,db_consumer.consumer_name,db_consumer.wenexa_id,db_consumer.rr_no into tmp_table from db_consumer_pump_details inner join db_consumer on db_consumer.consumer_id=db_consumer_pump_details.consumer_id
where db_consumer_pump_details.consumer_id=i and db_consumer_pump_details.status=$1--Point A
order by db_consumer_pump_details.consumer_id,pump_id,createddate desc limit 2
END LOOP;
return tmp_table
END;
$$
LANGUAGE plpgsql;
However Im not sure about my approach and whether im right at the points A and B as I've marked in the code above.And getting a load of errors while trying to create the temporary table.
EDIT: got the function to work ,but I get the following error when I try to run the function.
ERROR: array value must start with "{" or dimension information
Here's my revised function.
create temp table tmp_table(objectid integer,pump_id integer,pump_serial_id varchar(50),repdate timestamp with time zone,pumpmake varchar(50),status varchar(2),consumer_name varchar(50),wenexa_id varchar(50),rr_no varchar(25));
select consumer_id into cons_id from db_consumer_pump_details;
FOR i in select * from cons_id LOOP
insert into tmp_table
select objectid,pump_id,pump_serial_id,repdate,pumpmake,db_consumer_pump_details.status,db_consumer.consumer_name,db_consumer.wenexa_id,db_consumer.rr_no from db_consumer_pump_details inner join db_consumer on db_consumer.consumer_id=db_consumer_pump_details.consumer_id where db_consumer_pump_details.consumer_id=i and db_consumer_pump_details.status=$1
order by db_consumer_pump_details.consumer_id,pump_id,createddate desc limit 2;
END LOOP;
return query (select * from tmp_table);
drop table tmp_table;
END;
$$
LANGUAGE plpgsql;
AFAIK one can't declare tables as variables in postgres. What you can do is create one in your funcion body and use it thourough (or even outside of function). Beware though as temporary tables aren't dropped until the end of the session or commit.
The way to go is to use RETURN NEXT or RETURN QUERY
As for the function result type I always found RETURNS TABLE to be more readable.
edit:
Your cons_id array is innecessary, just iterate the values returned by select.
Also you can have multiple return query statements in a single function to append result of the query to the result returned by function.
In your case:
CREATE OR REPLACE FUNCTION getPumps(status varchar)
RETURNS TABLE (objectid INTEGER,pump_id INTEGER,pump_serial_id INTEGER....)
AS
$$
BEGIN
FOR i in SELECT consumer_id FROM db_consumer_pump_details LOOP
RETURN QUERY(
SELECT objectid,pump_id,pump_serial_id,repdate,pumpmake,db_consumer_pump_details.status,db_consumer.consumer_name,db_consumer.wenexa_id,db_consumer.rr_no FROM db_consumer_pump_details INNER JOIN db_consumer ON db_consumer.consumer_id=db_consumer_pump_details.consumer_id
WHERE db_consumer_pump_details.consumer_id=i AND db_consumer_pump_details.status=$1
ORDER BY db_consumer_pump_details.consumer_id,pump_id,createddate DESC LIMIT 2
);
END LOOP;
END;
$$
edit2:
You probably want to take a look at this solution for groupwise-k-maximum problem as that's exactly what you're dealing with here.
it might be easier to just return a table (or query)
CREATE FUNCTION extended_sales(p_itemno int)
RETURNS TABLE(quantity int, total numeric) AS $$
BEGIN
RETURN QUERY SELECT quantity, quantity * price FROM sales
WHERE itemno = p_itemno;
END;
$$ LANGUAGE plpgsql;
(copied from postgresql docs)

how would i print out multiple columns from a stored procedure in plpgsql

I am trying to print multiple columns in a stored procedure...can anyone please provide me with an example that uses a query. Thank you.
K for example, I have a movie database and I want to find the percentage of the profitable movies of all movies since the year 1960. I have the queires that do that and I ran it on pgADMIN and it works perfectly; however when i try creating a stored procedure, I know I have to use create a type holder as ( yr INTEGER, prnct FLOAT).
So now with that I need to create a stored procedure that would return the two columns one of the year and one of the percent, however how do i that this column is the yr and the next column is prnct.
If you want to return a single row with multiple columns than you can use record or some_table as the type.
If you have a table like movie than you can create a function like this:
CREATE OR REPLACE FUNCTION get_profitable_movie() RETURNS movie AS
If you want to return some arbitrary type, than you'll have to do something like this:
CREATE OR REPLACE FUNCTION get_profitable_movie() RETURNS record AS
And if you want to return more than 1 row, you have to use the SETOF modifier like this:
CREATE OR REPLACE FUNCTION get_profitable_movie() RETURNS SETOF record AS
You can create a function like this:
CREATE OR REPLACE FUNCTION multicolumn_thing() RETURNS record AS $$
DECLARE
r record;
BEGIN
SELECT 1, 2, 3 INTO r;
RETURN r;
END
$$ LANGUAGE 'plpgsql';
And select results from it like this:
SELECT
columns.a,
columns.b,
columns.c
FROM multicolumn_thing() AS columns(a int, b int, c int);
With a setof it's the same but multiple rows ofcourse :)