PostgreSQL - Array as function parameter - arrays

I want to know if it's posible to do something like this:
CREATE OR REPLACE function my_funct(some_data some_type, array int[])
RETURNS TABLE(code int, desc varchar)
LANGUAGE plpgsql
as $function$
DECLARE
..
BEGIN
WHILE some_condition < array.size --i know this doesn't exists
LOOP
INSERT INTO some_table values(array_data[1]); --I want to insert data from
the array as long as it has data
END LOOP;
RETURN QUERY select 1001, cast ('DONE!' as varchar);
END;
$function$
i would appreciate any response!
thanks!

A loop is typically not very efficient, use unnest() and insert the result in a single query:
CREATE OR REPLACE function my_funct(some_data some_type, array_data int[])
RETURNS TABLE(code int, descr varchar)
LANGUAGE plpgsql
as $function$
DECLARE
..
BEGIN
INSERT INTO some_table
select d.x
from unnest(array_data) as d(x);
RETURN QUERY select 1001, 'DONE!';
END;
$function$
You can however loop through an array as documented in the manual
BEGIN
FOREACH x IN ARRAY array_data
LOOP
INSERT INTO some_table values (x);
END LOOP;
RETURN QUERY select 1001, 'DONE!';
END;
That will be a lot slower though.

Related

How to convert this Postgresql code to Snowflake? Or How is it written in Snowflake?

Create function get_car_Price(Price_from int, Price_to int)
returns int
language plpgsql
as
$$
Declare
Car_count integer;
Begin
select count(*)
into Car_count
from Car
where Car_price between Price_from and Price_to;
return Car_count;
End;
$$;
The translation is straighforward:
CREATE FUNCTION get_car_Price(PRICE_FROM int, PRICE_TO int)
RETURNS INT
AS 'SELECT COUNT(*) FROM CAR WHERE Car_price BETWEEN PRICE_FROM AND PRICE_TO';
Function call:
SELECT get_car_price(100,200);
-- 0
Related: Scalar SQL UDFs

Return BIGINT[] FROM postgresql function

I know that some similar questions have been asked before but none of the answers worked for me!
I use POSTGRESQL 8.4 and am trying to return an array of BIGINT values from a function.
My query looks like:
CREATE OR REPLACE FUNCTION public.bigint_func(
in "in_arg" BIGINT)
RETURNS SETOF BIGINT
AS
$body$
DECLARE bigint_list BIGINT [ ];
BEGIN
SELECT
id
FROM
table1
INTO bigint_list;
RETURN NEXT;
END
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER;
and I'd like to use that function as below:
SELECT *
FROM
table1
JOIN (SELECT ids
FROM bigint_func(123))t2 ON table1.id = t2.id
but 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.
How should I write the code for the function?
Looks like you might be blending two ways of doing what you are trying to accomplish. Either loop through and return a value at a time, or return everything as a query.
I think either of these will work.
Loop and return a row at a time.
CREATE OR REPLACE FUNCTION bigint_func(
in "in_arg" BIGINT)
RETURNS SETOF BIGINT
AS
$body$
DECLARE
bigint_list BIGINT [ ];
my_id bigint;
BEGIN
for my_id in
SELECT
id
FROM
table1
loop
RETURN NEXT my_id;
END loop;
end;
$body$
LANGUAGE 'plpgsql'
VOLATILE
Or return the entire query as a dataset.
CREATE OR REPLACE FUNCTION bigint_func(
in "in_arg" BIGINT)
RETURNS SETOF BIGINT
AS
$body$
DECLARE
bigint_list BIGINT [ ];
BEGIN
return query
SELECT
id
FROM
table1;
end;
$body$
LANGUAGE 'plpgsql'
VOLATILE
-- EDIT --
To get the query working, maybe change the return type to a table:
CREATE OR REPLACE FUNCTION bigint_func(in "in_arg" BIGINT)
RETURNS table (id bigint)
AS
$body$
BEGIN
return query
SELECT
table1.id
FROM
table1;
end;
$body$
LANGUAGE 'plpgsql'
VOLATILE;
Then you can name the field anything you want (id in this case)
SELECT *
FROM
table1
join bigint_func(123) t2
ON table1.id = t2.id

Accessing the Return Table in a Postgres Function

In MSSQL when inside a multi-statement table valued function you can interact w/ the table as shown below.
CREATE FUNCTION dbo.test_func() RETURNS #table TABLE(id INT) AS
BEGIN
INSERT INTO #table(id)
SELECT 1 UNION SELECT 2 UNION SELECT 3
UPDATE #table SET id = -1 WHERE id = 3
RETURN
END
GO
Is there a way to accomplish this in Postgres 9.5? I'm not sure what to update as shown below.
CREATE FUNCTION test_func() RETURNS TABLE(id int) AS $$
BEGIN
return QUERY SELECT 1 UNION SELECT 2 UNION SELECT 3;
UPDATE ???? SET id = -1 WHERE id = 3;
END;
$$ LANGUAGE plpgsql STABLE;
You cannot change a function's result set after it's sent with RETURN NEXT or RETURN QUERY.
But in PostgreSQL, you are not forced to send the whole result-set in a single statement (that's why, what you asking makes little sense in PostgreSQL). You can send rows to the result-set row-by-row with RETURN NEXT, you can send chunks of the result-set with RETURN QUERY/RETURN QUERY EXECUTE, or you can even mix that. (You can also exit from that function with a single RETURN without parameters).
So, probably what you want to do is something like:
CREATE FUNCTION test_func() RETURNS TABLE(id int) AS $$
BEGIN
RETURN QUERY VALUES (1), (2);
-- do some calculation
RETURN NEXT -1;
END;
$$ LANGUAGE plpgsql STABLE;
If you really want to mimic what MSSQL does, you can use temporary tables, or (preferably) sub-selects inside functions:
CREATE FUNCTION test_func() RETURNS TABLE(id int) AS $$
BEGIN
RETURN QUERY SELECT (SELECT CASE WHEN v = 3 THEN -1 ELSE v END res)
FROM (VALUES (1), (2), (3)) v;
END;
$$ LANGUAGE plpgsql STABLE;
CREATE FUNCTION test_func()
RETURNS TABLE(id int) AS
$func$
BEGIN
RETURN QUERY
SELECT CASE WHEN v = 3 THEN -1 ELSE v END
FROM (VALUES (1), (2), (3)) v;
END
$func$ LANGUAGE plpgsql IMMUTABLE;
Which does not require PL/pgSQL at all. A simple SQL function will do:
CREATE FUNCTION test_func()
RETURNS SETOF int AS
$func$
SELECT CASE WHEN v = 3 THEN -1 ELSE v END
FROM (VALUES (1), (2), (3)) v;
$func$ LANGUAGE sql IMMUTABLE;
Also demonstrating the simple form SETOF int instead of TABLE(id int) for the simple case. You can use either.
Related answers (among many others):
PostgreSQL function returning multiple result sets
How to return result of a SELECT inside a function in PostgreSQL?
For more complex operations you could use a CTE
CREATE FUNCTION test_func()
RETURNS SETOF int AS
$func$
WITH v(id) AS (VALUES (1), (2), (3))
SELECT CASE WHEN id = 3 THEN -1 ELSE id END
FROM v
$func$ LANGUAGE sql IMMUTABLE;
For even more sophisticated jobs, you could work with an actual temporary table:
PostgreSQL table variable
Select from a table variable

Can a cursor hold variables in PL/pgSQL?

I know that a cursor can encapsulate a query, but can it also point to a variable value or multiple ones?
For example:
declare
my cursor refcursor;
var_x varchar;
begin
var_x := (select x from table where id = 7);
open mycursor for select(var_x);
end;
Is this possible in PL/pgSQL?
Yes, it can be done:
CREATE OR REPLACE FUNCTION f_value_cursor(_curs refcursor, _id1 int, _id2 int)
RETURNS TABLE (col1 int, col2 text) AS
$func$
DECLARE
var_x text := (SELECT t.col2 FROM tbl t WHERE t.tbl_id = _id1);
BEGIN
OPEN _curs FOR SELECT var_x;
RETURN QUERY
SELECT t.col1, t.col2
FROM tbl t
WHERE t.tbl_id >= _id2;
END
$func$ LANGUAGE plpgsql;
A cursor is good for any query that returns rows - even if it returns a single constant like in the example.
Using an unbound cursor variable as parameter, you can pass a name for the cursor to the function.
I made the function return a table at the same time, since that seems to be what you are after in those last couple of questions.
As mentioned in my previous answer, it is essential that you fetch values from the cursor in the same transaction:
BEGIN;
SELECT * FROM f_value_cursor('mycursor', 1, 2);
FETCH ALL FROM mycursor;
ROLLBACK; -- or COMMIT
SQL Fiddle.
For the record: consider a temporary table instead, which lives for the duration of the session (per default), not just for the transaction. Tables are much more versatile. Cursors make more sense for huge result sets.

Postgres STRING_TO_ARRAY alternative? Like STRING_TO_RECORD?

I need to convert a comma separated text into a set of records. I created this function
but I am not convinced that the best way:
CREATE OR REPLACE FUNCTION F_StringListToRecord(pStringList TEXT, pDelimiter VARCHAR(10)) RETURNS SETOF RECORD AS $$
DECLARE
vIndex INT;
arrSize INT;
arrValue TEXT[];
BEGIN
arrValue := STRING_TO_ARRAY(pStringList, pDelimiter);
arrSize := ARRAY_UPPER(arrValue, 1);
FOR vIndex IN 1..arrSize LOOP
RETURN QUERY SELECT arrValue[vIndex];
END LOOP;
END $$
LANGUAGE plpgsql;
Is there any other function similar to STRING_TO_ARRAY (perhaps STRING_TO_RECORD)?
In 8.4 you can use:
select * from unnest(string_to_array(my_string_here,delimiter)) as v(x);

Resources