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
Related
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.
Wrote a function for accepting a Number and a user id and returning a table having individual digit in a single row with that User id. Function created successfully but when i tried to execute function its returning a blank table.
Attaching screenshot of function and output.
Create Function ExtractingDigits(#InputNumber INT, #UserId varchar)
RETURNS #ReturnTable Table(UserId Varchar(20), ModNumber Int)
as
begin
Declare #ModNumber Int
While #InputNumber!=0
begin
set #ModNumber= #InputNumber%10
set #InputNumber=#InputNumber/10
insert into #ReturnTable(UserId, ModNumber)
select * from #ReturnTable
end
return
end
select * from ExtractingDigits(123, 'as')
The fix is to use a VALUES clause for your INSERT rather than a SELECT from an empty table:
alter Function ExtractingDigits(#InputNumber INT, #UserId varchar)
RETURNS #ReturnTable Table(UserId Varchar(20), ModNumber Int)
as
begin
Declare #ModNumber Int
While #InputNumber!=0
begin
set #ModNumber= #InputNumber%10
set #InputNumber=#InputNumber/10
insert into #ReturnTable(UserId, ModNumber)
values (#UserId,#ModNumber)
end
return
end
go
select * from ExtractingDigits(123, 'as')
Just as style comments though, I'd also recommend a) changing either the parameter order or the table definition so that they're both consistent (i.e. have both be int, varchar or varchar, int rather than swapping) but b) recommend not including #UserId in this function at all. This would make the function much more generically usable:
alter Function ExtractingDigits(#InputNumber INT)
RETURNS #ReturnTable Table(ModNumber Int)
as
begin
Declare #ModNumber Int
While #InputNumber!=0
begin
set #ModNumber= #InputNumber%10
set #InputNumber=#InputNumber/10
insert into #ReturnTable(ModNumber)
values (#ModNumber)
end
return
end
go
select * from ExtractingDigits(123) cross apply (select 'as' as UserId)
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
I would like to build a very complicated Table_valued function (TVF) that will return non fixed output structure.
sometimes the TVF may return 2 columns and other times may return only 1 column.
I couldn't find a way to do this because the database engine requires explicit output table structure as:
RETURNS #returnTable
TABLE
(
column1 numeric,
column2 numeric
)
Once i find a solution for the above i would like to do something like:
SELECT
*
INTO #tmp
FROM MyTVF
I know its possible to implement that with stored procedure but then i will face to another problem. By using stored procedure i will not be able to save the result to a temp table without declaring the output explicitly.
Here is a shot example of what i would like to do:
CREATE FUNCTION [dbo].myFunction (#type int)
RETURNS #table TABLE
(
Column1 int,
Column2 int
)
AS
BEGIN
IF #type=1
BEGIN
INSERT INTO #table
SELECT 1 AS Column1, 2 AS Column2
END
ELSE
BEGIN
INSERT INTO #table
SELECT 1 AS OnlyOneColumn
END
RETURN
END
GO
SELECT * INTO #tmp1 FROM myFunction(1)
SELECT * FROM #tmp1
All Table-Valued Functions return a table with a fixed structure.
However (unlike Multi-Statement Table-Valued Functions), you don't have to declare that structure for an In-Line Table-Valued Function, for example:
CREATE FUNCTION MyFunction(#MyParameter INT)
RETURNS TABLE AS RETURN
SELECT * FROM SomeTable
WHERE SomeColumn=#MyParameter
This function will still return a fixed number of columns: even if you later add columns to SomeTable, they will not be returned by MyFunction, unless the function is modified or refreshed (with sp_refreshsqlmodule).
You cannot create a Table-Valued Function that returns a variable number of columns (depending on the input parameters).
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.