Make function by pspgsql pgadmin( In case ERROR: more than one row returned by a subquery used as an expression) - plpgsql

First pgr_dijkstra is
my code is
select di.seq, di.node , di.edge , di.cost, a.geom
from pgr_dijkstra(
'select id, target, source, cost from pedroad',
(select get_source2('location1')),array(select get_target2('points')),false)
as di,
pedroad a
where di.node = a.source;
cf
select get_source2('location1') is
select get_target2('points') is
If I ran this code
result is
Now I have a question.
I want to make function, so I try to like this.
my code
create or replace function get_route2(snode integer, tnode integer)
returns setof geometry as
$$
declare
snode1 integer;
tnode2 integer;
begin
snode1 := snode;
tnode2 := tnode;
return query
select di.seq, di.node, di.edge, di.cost, a.geom
from pgr_dijkstra(
'select id, target, source, cost from pedroad',
snode1, tnode2,false) as di,pedroad a
where di.node = a.source;
end
$$
language plpgsql;
Then I ran this function like this
select get_route2((select get_source2('location1')),(select get_target2('points')))
ERROR: more than one row returned by a subquery used as an expression
Sadly,, I know there are many errors in my code...
However, I don't know what to do... Please help us !

Related

Query has no destination for result data when try to make search filter [duplicate]

I am trying to fetch data from remote db by using dblink through function but getting an error "query has no destination for result data". I am using plpgsql language to do the same.
Function:
CREATE OR REPLACE FUNCTION fun()
RETURNS text AS
$$
begin
select dblink_connect(
'port=5432 dbname=test user=postgres password=****');
WITH a AS (
SELECT *
FROM dblink(
'SELECT slno,fname,mname,lname
FROM remote_tbl'
) AS t (slno int, fname text, mname text, lname text)
)
, b AS (
INSERT INTO temptab1
SELECT slno, name
FROM a
)
, c AS (
INSERT INTO temptab2
SELECT slno, name
FROM a
)
INSERT INTO temptab3
SELECT slno, name
FROM a;
select dblink_disconnect();
end;
$$
LANGUAGE plpgsql;
Calling Function:
select fun();
Error: query has no destination for result data
The stored procedure won't just return the result of the last SELECT. You need to actually return the value:
CREATE OR REPLACE FUNCTION fun() RETURNS text AS $$
BEGIN
--- ....
RETURN(SELECT dblink_disconnect());
END
$$ LANGUAGE plpgsql;
You're getting the error because Postgres expects the function to return something of type text, but your function doesn't return anything.
Use a plain SQL function instead of PL/PgSQL, or use SELECT INTO and ordinary RETURN.
Reason for the error you're getting is because there is no return in between your BEGIN and END for example:
BEGIN
update mytable set lastcodeused = to_char(cast(lastcodeused as INTEGER)+1, 'FM999999999999999999') where
classid = classIdVar and appid= appIdInt
RETURNING concat(pageUniqueCode,lastcodeused) as pageUniqueCode
into taskcode;
return taskcode;
END;
If you have this error using a pgplsql procedure or function, and you are sure that the return is defined correctly, there exists a different non-intuitive cause. I needed some time to realize this so I think it is worth sharing. I had a function like this:
CREATE OR REPLACE FUNCTION "db".fn_x(
id integer)
RETURNS TABLE(b_val varchar(100), c_val varchar(100))
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
var_b_val varchar(100);
var_c_val varchar(100);
BEGIN
select var_b, var_c
-- Missing INTO clause was the cause of the error.
var_b_val, var_c_val
from "db".table_y where y_id = id;
return query(select var_b_val, var_c_val);
END;
$BODY$;
Just adding that missing INTO clause made the function work correctly.
In conclusion, this error can also trigger on silent syntax errors.

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: Query has no destination for result data

I am trying to fetch data from remote db by using dblink through function but getting an error "query has no destination for result data". I am using plpgsql language to do the same.
Function:
CREATE OR REPLACE FUNCTION fun()
RETURNS text AS
$$
begin
select dblink_connect(
'port=5432 dbname=test user=postgres password=****');
WITH a AS (
SELECT *
FROM dblink(
'SELECT slno,fname,mname,lname
FROM remote_tbl'
) AS t (slno int, fname text, mname text, lname text)
)
, b AS (
INSERT INTO temptab1
SELECT slno, name
FROM a
)
, c AS (
INSERT INTO temptab2
SELECT slno, name
FROM a
)
INSERT INTO temptab3
SELECT slno, name
FROM a;
select dblink_disconnect();
end;
$$
LANGUAGE plpgsql;
Calling Function:
select fun();
Error: query has no destination for result data
The stored procedure won't just return the result of the last SELECT. You need to actually return the value:
CREATE OR REPLACE FUNCTION fun() RETURNS text AS $$
BEGIN
--- ....
RETURN(SELECT dblink_disconnect());
END
$$ LANGUAGE plpgsql;
You're getting the error because Postgres expects the function to return something of type text, but your function doesn't return anything.
Use a plain SQL function instead of PL/PgSQL, or use SELECT INTO and ordinary RETURN.
Reason for the error you're getting is because there is no return in between your BEGIN and END for example:
BEGIN
update mytable set lastcodeused = to_char(cast(lastcodeused as INTEGER)+1, 'FM999999999999999999') where
classid = classIdVar and appid= appIdInt
RETURNING concat(pageUniqueCode,lastcodeused) as pageUniqueCode
into taskcode;
return taskcode;
END;
If you have this error using a pgplsql procedure or function, and you are sure that the return is defined correctly, there exists a different non-intuitive cause. I needed some time to realize this so I think it is worth sharing. I had a function like this:
CREATE OR REPLACE FUNCTION "db".fn_x(
id integer)
RETURNS TABLE(b_val varchar(100), c_val varchar(100))
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
var_b_val varchar(100);
var_c_val varchar(100);
BEGIN
select var_b, var_c
-- Missing INTO clause was the cause of the error.
var_b_val, var_c_val
from "db".table_y where y_id = id;
return query(select var_b_val, var_c_val);
END;
$BODY$;
Just adding that missing INTO clause made the function work correctly.
In conclusion, this error can also trigger on silent syntax errors.

Returning result set from Postgres functions

In my Postgres 9.2 database, I need to build a function that takes several parameters, performs several queries, and then returns a data set that is composed of several rows and several columns. I've built several test functions to get a better grasp of Postgres' functionality, here is one:
CREATE OR REPLACE FUNCTION sql_with_rows11(id integer) RETURNS character varying AS
$BODY$
declare vid integer;
declare vendor character varying;
BEGIN
vid := (select v_id from public.gc_alerts where a_id = id);
vendor := (select v_name from public.gc_vendors where v_id = vid);
RETURN vendor;
END;
$BODY$
LANGUAGE plpgsql;
I know that I can combine this into one query, but this is more of a practice exercise. This works fine and I get the vendor name. However, I need to return more than one column from the gc_vendors table.
Ultimately, I need to return columns from several tables based on subqueries. I've looked into creating a result set function, but I believe it only returns one row at a time. I also looked into returning setof type, but that seems to be limited to existing tables.
After initial feedback, I changed the function to the following:
CREATE OR REPLACE FUNCTION sql_with_rows14(IN v_uid character varying, IN lid integer)
RETURNS table (aid int, aname character varying) AS
$BODY$
declare aid integer;
declare aname character varying;
BEGIN
sql_with_rows14.aid := (select a_id from public.gc_alerts where v_id = sql_with_rows14.v_uid);
sql_with_rows14.aname := (select a_name from public.gc_alerts where a_id = sql_with_rows14.aid);
RETURN;
END;
$BODY$
LANGUAGE plpgsql;
I also tried RETURN NEXT, but same results.
When I query it, if the query returns only one row, it works fine. However it doesn't work for multiple rows. I also tried something like this, with the same result:
...
BEGIN
sql_with_rows14.aid := (select a_id from public.gc_alerts);
sql_with_rows14.aname := (select a_name from public.gc_alerts);
RETURN NEXT;
END;
I need to return more than one column from the gc_vendors table
To return a single row with multiple fields (as opposed to a set of rows), you can either use:
RETURNS row_type
.. where row_type is a pre-defined composite type (like a table name, that serves as such automatically). Or:
RETURNS record
combined with OUT parameters. Be aware that OUT parameters are visible in the body almost everywhere and avoid naming conflicts.
Using the second option, your function could look like this:
CREATE OR REPLACE FUNCTION sql_with_columns(IN _id integer -- IN is optional default
, OUT vid integer
, OUT vendor text)
RETURNS record
LANGUAGE plpgsql AS
$func$
BEGIN
SELECT INTO vid v_id
FROM public.gc_alerts
WHERE a_id = id;
SELECT INTO vendor v_name
FROM public.gc_vendors
WHERE v_id = vid;
RETURN; -- just noise, since OUT parameters are returned automatically
END
$func$;
As you mentioned, you should combine both queries into one, or even use a plain SQL statement instead. This is just a show case. The excellent manual has all the details.
You can also use:
RETURNS TABLE (...)
Or:
RETURNS SETOF row_type
This allows to return a set of rows (0, 1 or many). But that's not in your question.
To get individual columns instead of a record representation, call the function with:
SELECT * FROM sql_with_columns(...);
There are lots of examples here on SO, try a search - maybe with additional key words.
Also read the chapter "Returning from a Function" in the manual.
First of all, consider using views or simple queries. I'd say that if you can process something with a simple query, you shouldn't create function for that. in your case, you can use this query
select
v.v_name, v.* -- or any other columns from gc_alerts or gc_vendors
from public.gc_alerts as a
inner join public.gc_vendors as v on v.v_id = a.vid
where a.a_id = <your id here>
if you want your function to return rows, you can declare it like
CREATE OR REPLACE FUNCTION sql_with_rows11(id integer)
RETURNS table(vendor text, v_id int)
as
$$
select
v.v_name, v.v_id
from public.gc_alerts as a
inner join public.gc_vendors as v on v.v_id = a.vid
where a.a_id = id
$$ language SQL;
or plpgsql function:
CREATE OR REPLACE FUNCTION sql_with_rows11(id integer)
RETURNS table(vendor text, vid int)
AS
$$
declare vid integer;
declare vendor character varying;
BEGIN
sql_with_rows11.vid := 1; -- prefix with function name because otherwise it would be declared variables
sql_with_rows11.vendor := 4;
return next;
sql_with_rows11.vid := 5;
sql_with_rows11.vendor := 8;
return next;
END;
$$ LANGUAGE plpgsql;
sql fiddle demo to fiddle with :)

How to return multiple rows from PL/pgSQL function?

I have spent good amount of time trying to figure it out and I haven't been able to resolve it. So, I need your help please.
I am trying to write a PL/pgSQL function that returns multiple rows. The function I wrote is shown below. But it is not working.
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS SETOF RECORD
AS
$$
DECLARE result_record keyMetrics;
BEGIN
return QUERY SELECT department_id into result_record.visits
from fact_department_daily
where report_date='2013-06-07';
--return result_record;
END
$$ LANGUAGE plpgsql;
SELECT * FROM get_object_fields;
It is returning this error:
ERROR: RETURN cannot have a parameter in function returning set;
use RETURN NEXT at or near "QUERY"
After fixing the bugs #Pavel pointed out, also define your return type properly, or you have to provide a column definition list with every call.
This call:
SELECT * FROM get_object_fields()
... assumes that Postgres knows how to expand *. Since you are returning anonymous records, you get an exception:
ERROR: a column definition list is required for functions returning "record"
One way (of several) to fix this is with RETURNS TABLE (Postgres 8.4+):
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS TABLE (department_id int) AS
$func$
BEGIN
RETURN QUERY
SELECT department_id
FROM fact_department_daily
WHERE report_date = '2013-06-07';
END
$func$ LANGUAGE plpgsql;
Works for SQL functions just the same.
Related:
PostgreSQL: ERROR: 42601: a column definition list is required for functions returning "record"
I see more bugs:
first, a SET RETURNING FUNCTIONS call has following syntax
SELECT * FROM get_object_fields()
second - RETURN QUERY forwards query result to output directly. You cannot store this result to variable - it is not possible ever in PostgreSQL now.
BEGIN
RETURN QUERY SELECT ....; -- result is forwarded to output directly
RETURN; -- there will not be any next result, finish execution
END;
third - these simple functions is better to implement in SQL languages
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS SETOF RECORD AS $$
SELECT department_id WHERE ...
$$ LANGUAGE sql STABLE;
Here's one way
drop function if exists get_test_type();
drop type if exists test_comp;
drop type if exists test_type;
drop type if exists test_person;
create type test_type as (
foo int,
bar int
);
create type test_person as (
first_name text,
last_name text
);
create type test_comp as
(
prop_a test_type[],
prop_b test_person[]
);
create or replace function get_test_type()
returns test_comp
as $$
declare
a test_type[];
b test_person[];
x test_comp;
begin
a := array(
select row (m.message_id, m.message_id)
from message m
);
-- alternative 'strongly typed'
b := array[
row('Bob', 'Jones')::test_person,
row('Mike', 'Reid')::test_person
]::test_person[];
-- alternative 'loosely typed'
b := array[
row('Bob', 'Jones'),
row('Mike', 'Reid')
];
-- using a select
b := array (
select row ('Jake', 'Scott')
union all
select row ('Suraksha', 'Setty')
);
x := row(a, b);
return x;
end;
$$
language 'plpgsql' stable;
select * from get_test_type();
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS table (department_id integer)
AS
$$
DECLARE result_record keyMetrics;
BEGIN
return QUERY
SELECT department_id
from fact_department_daily
where report_date='2013-06-07';
--return result_record;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM get_object_fields()