I have one function that returns all employee IDs
Function definition is like this:
CREATE OR REPLACE FUNCTION tmp()
RETURNS setof record AS
$func$
begin
select emp_id from employee_master;
end;
$func$
LANGUAGE plpgsql;
But when i call this function using
select * from tmp() as abc(emp_id text);
It gives error like
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function "tmp" line 3 at SQL statement
Please give solution :)
If you want to return a rowset from a PL/PgSQL function you must use RETURN - in this case, probably RETURN QUERY:
RETURN QUERY SELECT emp_id FROM employee_master;
I don't see the point of having this in a PL/PgSQL function at all, though.
Make the function a plain SQL one as in:
...
LANGUAGE SQL;
It is much more practical to declare the actual type of the column instead of the unwieldy record type. Assuming emp_id to be integer, a simple SQL function could look like this:
CREATE OR REPLACE FUNCTION tmp()
RETURNS SETOF integer AS
$func$
SELECT emp_id FROM employee_master
$func$ LANGUAGE sql;
However, the error message in your comment does not match the given question. Depending on your actual requirements, you would adjust the RETURN type.
Related
I'm new to databases and SQL and i've been messing around with functions on POSTGRESQL and I made a simple function to select all names from a table.
But the function, when called, returns just a single row as opposed to all rows being returned when i use SELECT c_name FROM customers; instead of the code shown below:
CREATE OR REPLACE FUNCTION get_cust_names()
RETURNS varchar AS
$body$
SELECT c_name FROM customers;
$body$
LANGUAGE SQL
Function call
SELECT get_cust_names()
this returns just a SINGLE row and this isnt code that i will use in a project etc. I'm just curious as to why postgresql behaves this way.
You need to declare the function as returns table(..) or returns setof
CREATE OR REPLACE FUNCTION get_cust_names()
RETURNS table(cust_name varchar)
AS
$body$
SELECT c_name
FROM customers;
$body$
LANGUAGE SQL;
Then use it like a table:
select *
from get_cust_names();
I have problem with return table using SQL Procedure.
My code:
CREATE PROCEDURE return_data(surname character varying)
LANGUAGE SQL
AS $$
SELECT * FROM peopple WHERE surname=surname
$$;
CALL return_data('Jobs');
Currently the procedure executes without error but it doesn't return a table.
Procedures aren't meant to return anything. Use a set returning function
create function return_data(p_surname varchar)
returns setof people
as
$$
select *
from people
where surname = p_surname;
$$
language sql;
You should also avoid parameter or variable names that have the same name as a column.
Then use it like this:
select *
from return_data('Jobs');
You can append one or more rows to the result set:
CREATE OR REPLACE FUNCTION return_data(surname character varying)
RETURNS TABLE(firstname character varying, surname character varying, age integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT firstname, surname, age
FROM users;
END;
$function$
;
Invoke the function:
SELECT * FROM return_data("a surname");
A function can return but a procedure cannot. Thats true, I wanna add to that we generally use function to return values of one type (in general one value)
While,
Procedure never returns but can behave like a function without a return statement using OUT parameters. To add to this we use procedure when we dont wanna return in actual but executing sql statements but in case if we require a procedure to give multiple values we can make procedure behave like a function for multole values output vja OUT.
As a table can be accessed by record or array type depending upon homogenity. Therefore, use function with return type as record/array.
Hence, as function is solely used to return single output we have to specify via return.
I am trying to create a function like this:
I tried changing the return type to int or text etc. to see if the code works outside of that, but it doesn't. I am a beginner in PostgreSQL so please don't be harsh if I missed something obvious.
create or replace function date_select(i INT) returns void as
$$
begin
select * from dwh_stg.stg_dce_gift where gift_id = i;
end
$$ language plpgsql
select date_select(16940)
SQL Error [42601]:
ERROR: query has no destination for result data
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Where: PL/pgSQL function date_select(integer) line 3 at SQL statement
If you want to return something, you need to define the function to return something (not void)
Apparently you want to return multiple rows from the table stg_dec_gift, for that you need to define the function as returns setof dwh_stg.stg_dce_gift. For a simple function encapsulating a query there is not need to use PL/pgSQL a plain SQL function will do just fine:
create or replace function date_select(i INT)
returns setof dwh_stg.stg_dce_gift --<< here
as
$$
select *
from dwh_stg.stg_dce_gift
where gift_id = i;
$$
stable
language sql;
Then use it in the FROM part:
select *
from date_select(16940);
Online example: https://rextester.com/WYDCE44062
I've got a function to return relevant position of an array matching given value like below:
CREATE OR REPLACE FUNCTION get_index(needle ANYELEMENT, haystack ANYARRAY)
RETURNS TABLE (i_position int)
AS $$
SELECT
i+i_step AS i_position
FROM (VALUES(1),(2)) steps(i_step),
generate_series(array_lower($2,1), array_upper($2,1)) AS i
WHERE $2[i] = $1
$$ LANGUAGE SQL STABLE;
Instead of passing a single value to the function, I want to pass a table name, as one column of the table would be used to do the value comparison (WHERE $2[i] = $1 ), instead of a single value passed to the function. However, it doesn't seem like the function support SQL using argument as table name.
I'm wondering if there's alternative. I'd like to use SQL function instead of PLPGSQL, for the sake of performance. As our table is huge.
I'd like to achieve something like below:
CREATE OR REPLACE FUNCTION get_index(tbl ANYELEMENT, haystack ANYARRAY)
RETURNS TABLE (i_position int)
AS $$
SELECT
i+i_step AS i_position
FROM (VALUES(1),(2)) steps(i_step),
generate_series(array_lower($2,1), array_upper($2,1)) AS i,
$1
WHERE $2[i] = $1.col1
$$ LANGUAGE SQL STABLE;
It is not possible in SQL function - It doesn't support dynamic sql. You have to use PLpgSQL - EXECUTE statement.
SQL function is faster than PLpgSQL only when inlining is successful. When it isn't successful, and SQL function should be evaluated, then PLpgSQL should not be slower. When body of SQL function contains SRF functions like generate_series, then inlining is not effective.
I would like to use a plpgsql function with a table and several columns as input parameter. The idea is to split the table in chunks and do something with each part.
I tried the following function:
CREATE OR REPLACE FUNCTION my_func(Integer)
RETURNS SETOF my_part
AS $$
DECLARE
out my_part;
BEGIN
FOR i IN 0..$1 LOOP
FOR out IN
SELECT * FROM my_func2(SELECT * FROM table1 WHERE id = i)
LOOP
RETURN NEXT out;
END LOOP;
END LOOP;
RETURN;
END;
$$
LANGUAGE plpgsql;
my_func2() is the function that does some work on each smaller part.
CREATE or REPLACE FUNCTION my_func2(table1)
RETURNS SETOF my_part2 AS
$$
BEGIN
RETURN QUERY
SELECT * FROM table1;
END
$$
LANGUAGE plpgsql;
If I run:
SELECT * FROM my_func(99);
I guess I should receive the first 99 IDs processed for each id.
But it says there is an error for the following line:
SELECT * FROM my_func2(select * from table1 where id = i)
The error is:
The subquery is only allowed to return one column
Why does this happen? Is there an easy way to fix this?
There are multiple misconceptions here. Study the basics before you try advanced magic.
Postgres does not have "table variables". You can only pass 1 column or row at a time to a function. Use a temporary table or a refcursor (like commented by #Daniel) to pass a whole table. The syntax is invalid in multiple places, so it's unclear whether that's what you are actually trying.
Even if it is: it would probably be better to process one row at a time or rethink your approach and use a set-based operation (plain SQL) instead of passing cursors.
The data types my_part and my_part2 are undefined in your question. May be a shortcoming of the question or a problem in the test case.
You seem to expect that the table name table1 in the function body of my_func2() refers to the function parameter of the same (type!) name, but this is fundamentally wrong in at least two ways:
You can only pass values. A table name is an identifier, not a value. You would need to build a query string dynamically and execute it with EXECUTE in a plpgsql function. Try a search, many related answers her on SO. Then again, that may also not be what you wanted.
table1 in CREATE or REPLACE FUNCTION my_func2(table1) is a type name, not a parameter name. It means your function expects a value of the type table1. Obviously, you have a table of the same name, so it's supposed to be the associated row type.
The RETURN type of my_func2() must match what you actually return. Since you are returning SELECT * FROM table1, make that RETURNS SETOF table1.
It can just be a simple SQL function.
All of that put together:
CREATE or REPLACE FUNCTION my_func2(_row table1)
RETURNS SETOF table1 AS
'SELECT ($1).*' LANGUAGE sql;
Note the parentheses, which are essential for decomposing a row type. Per documentation:
The parentheses are required here to show that compositecol is a column name not a table name
But there is more ...
Don't use out as variable name, it's a keyword of the CREATE FUNCTION statement.
The syntax of your main query my_func() is more like psudo-code. Too much doesn't add up.
Proof of concept
Demo table:
CREATE TABLE table1(table1_id serial PRIMARY KEY, txt text);
INSERT INTO table1(txt) VALUES ('a'),('b'),('c'),('d'),('e'),('f'),('g');
Helper function:
CREATE or REPLACE FUNCTION my_func2(_row table1)
RETURNS SETOF table1 AS
'SELECT ($1).*' LANGUAGE sql;
Main function:
CREATE OR REPLACE FUNCTION my_func(int)
RETURNS SETOF table1 AS
$func$
DECLARE
rec table1;
BEGIN
FOR i IN 0..$1 LOOP
FOR rec IN
SELECT * FROM table1 WHERE table1_id = i
LOOP
RETURN QUERY
SELECT * FROM my_func2(rec);
END LOOP;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM my_func(99);
SQL Fiddle.
But it's really just a a proof of concept. Nothing useful, yet.
As the error log is telling you.. you can return only one column in a subquery, so you have to change it to
SELECT my_func2(SELECT Specific_column_you_need FROM hasval WHERE wid = i)
a possible solution can be that you pass to funct2 the primary key of the table your funct2 needs and then you can obtain the whole table by making the SELECT * inside the function