Function postgreSQL with JSON parameters - sql

I have to create a function that takes an object of this type :
"users":"John",
"Level":"1",
"list":[
{"id":"1", "price1":"100.50", "price2":"90.50"},
{"id":"2", "price1":"100.60", "price2":"80.50"},
{"id":"2", "price1":"105.50", "price2":"700.50"}
]}
For information JSON is not obliged but it is the thing that seemed the simplest to me. But maybe the creation of a POSTGRES type can work.
Then how to process the object if I use a JSON or PostGres Type.
CREATE OR REPLACE FUNCTION MYFUNCTION(myobject JSON ? CREATE TYPE ? )
RETURNS TABLE (
"OK" BOOLEAN,
"NB" VARCHAR
)
AS $$
DECLARE
BEGIN
-- I would like to get for each object in the list the price1 and price2 to
compare them.
END; $$
LANGUAGE 'plpgsql';

The crux of the question seems to be how to extract the values from the json object. This is one way:
select * from json_to_recordset('{
"users":"John",
"Level":"1",
"list":[
{"id":"1", "price1":"100.50", "price2":"90.50"},
{"id":"2", "price1":"100.60", "price2":"80.50"},
{"id":"2", "price1":"105.50", "price2":"700.50"}
]}'::json->'list') as foo(id int, price1 numeric, price2 numeric);
With a json variable instead of the literal string:
select * from json_to_recordset(jsonvariable->'list') as foo(id int, price1 numeric, price2 numeric)
Note. The json object you provide isn't legal. Some commas missing. I'd also suggest you use jsonb instead of json.
Edited:This is a skeleton on how you can use this in a plpgsql function:
create or replace function func(jsonvariable json) returns table (ok boolean, nb text) as
$BODY$
declare
r record;
begin
for r in (select * from json_to_recordset(jsonvariable->'list') as foo(id int, price1 numeric, price2 numeric)) loop
--- DO THINGS
ok:=(r.price1>r.price2);
nb:='this is text returned';
return next;
end loop;
end;
$BODY$
language plpgsql;

No Need to use loop for processing on the JSON ARRAY. you can use JSONB_ARRAY_ELEMENTS or JSONB_TO_RECORDSET for your requirement like below:
Using JSONB_TO_RECORDSET
CREATE OR REPLACE FUNCTION MYFUNCTION(myobject JSONB)
RETURNS TABLE (ok BOOLEAN, nb VARCHAR)
AS
$$
BEGIN
return query
select
price1>price2, -- you can change the condition here
id::varchar -- you can change the value as per your requirement
from jsonb_to_recordset(myobject ->'list') as x(id int, price1 numeric, price2 numeric);
END;
$$
LANGUAGE 'plpgsql';
Using JSONB_ARRAY_ELEMENTS
CREATE OR REPLACE FUNCTION MYFUNCTION1(myobject JSONB)
RETURNS TABLE (ok BOOLEAN, nb VARCHAR)
AS
$$
BEGIN
return query
select
(x->>'price1')::numeric > (x->>'price2')::numeric, -- you can change the condition here
(x->>'id')::varchar -- you can change the value as per your requirement
from jsonb_array_elements(myobject ->'list') as x;
END;
$$
LANGUAGE 'plpgsql';
DEMO

Related

Postgres function to return Table into variables

How can I capture different columns into different variables like so (note this is only pseudocode so I am assuming it will cause errors. Example taken from here)
create or replace function get_film (
p_pattern varchar
)
returns table (
film_title varchar,
film_release_year int
)
language plpgsql
as $$
begin
return query
select
title,
release_year::integer
from
film
where
title ilike p_pattern;
end;$$
create or replace function get_film_into_variables (
p_pattern varchar
)
returns null
language plpgsql
as $$
declare
v_title varchar,
v_release_year integer
begin
SELECT
get_film (p_pattern)
INTO
v_title,
v_release_year;
end;$$
Assuming you have some purpose for the variables after retrieving them not just ending the function your "get_film_into_variables" is almost there. But first let's backup just a bit. A function that returns a table does just that, you can use the results just like a table stored on disk (it just goes away after query or calling function ends). To that end only a slight change to the "get_film_into_variables" function is required. The "get_film" becomes the object of the FROM clause. Also change the returns null, to returns void. So
create or replace function get_film_into_variables (
p_pattern varchar
)
returns void
language plpgsql
as $$
declare
v_title varchar;
v_release_year integer;
begin
select *
from get_film (p_pattern)
INTO
v_title,
v_release_year;
end;
$$;
The above works for a single row returned by a function returning table. However for a return of multiple rows you process the results of the table returning function just lake you would an actual table - with a cursor.
create or replace
function get_film_into_variables2(p_pattern varchar)
returns void
language plpgsql
as $$
declare
k_message_template constant text = 'The film "%s" was released in %s.';
v_title varchar;
v_release_year integer;
v_film_message varchar;
c_film cursor (c_pattern varchar) for
select * from get_film (c_pattern);
begin
open c_film (p_pattern);
loop
fetch c_film
into v_title
, v_release_year;
exit when not found;
v_film_message = format( k_message_template,v_title,v_release_year::text);
raise notice using
message = v_film_message;
end loop;
end;
$$;
BTW: the get_film function can be turned into a SQL function. See fiddle here. For demo purposes get_film_into_variable routines return a message.

Is it allowed to write a SQL function in PostgreSQL to return more then one data type?

I am working on a compatibility issue, I want to create a SQL function which can return an int or a Varchar based on the conditions in the program.
For example, I have a function named foo.
CREATE or REPLACE FUNCTION foo(param VARCHAR(100))
-- Which datatype I should use here?
RETURNS SOME_DATATYPE AS
$$
SELECT
CASE UPPER(param)
WHEN 'varchar' THEN CAST((SELECT 'varchar_value') AS varchar)
WHEN 'int' THEN CAST((SELECT 1426598) AS int)
ELSE param||' is not supported.'
END
$$
LANGUAGE SQL;
For the following queries, I am expecting output as follows.
select pg_typeof(foo('varchar')) from dual;
--I am expecting varchar as output.
select pg_typeof(foo('int')) from dual;
--I am expecting int as output.
Please suggest if such a feature is there, Or any alternative I can try to achieve the same.
The closest thing would be to overload the function:
create or replace function foo(param text)
returns text
$$
select param;
$$
language sql;
create or replace function foo(param int)
returns int
$$
select param;
$$
language sql;
create or replace function foo(param date)
returns int
$$
select param;
$$
language sql;

How to create a procedure that returns a set of rows from a table in postgreSQL

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.

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)

Return a query from a function?

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;