I have the following plpgsql function that does work great on pg 8.3 and above but I need to translate it back to a pg 8.1 database and I can't seam to get it right.
Any tips? I need to get rid of the "RETURN QUERY" as it was not yet introduced in 8.1...
CREATE OR REPLACE FUNCTION specie_children (specie_id INT, self BOOLEAN)
RETURNS SETOF specie AS
$BODY$
DECLARE
r specie%ROWTYPE;
BEGIN
IF self THEN
RETURN QUERY SELECT * FROM specie WHERE specieid = specie_id;
END IF;
FOR r IN SELECT * FROM specie WHERE parent = specie_id
LOOP
RETURN NEXT r;
RETURN QUERY SELECT * FROM specie_children(r.specieid, FALSE);
END LOOP;
RETURN;
END
$BODY$
LANGUAGE 'plpgsql';
How do I translate this ?
RETURN QUERY SELECT * FROM specie_children(r.specieid, FALSE);
could be rewritten as
for r2 in select * from specie_children(r.specieid, FALSE)
loop
return next r2
end loop
Quick demo. Basically #maniek already provided the answer.
Test table:
CREATE TEMP TABLE specie(specieid int, parent int);
INSERT INTO specie VALUES
(1,0), (2,0), (3,0)
,(11,1), (12,1), (13,1)
,(111,11), (112,11), (113,11);
Rewritten function:
CREATE OR REPLACE FUNCTION specie_children (specie_id INT, self BOOLEAN)
RETURNS SETOF specie AS
$BODY$
DECLARE
r specie%ROWTYPE;
BEGIN
IF self THEN
FOR r IN SELECT * FROM specie WHERE specieid = $1
LOOP
RETURN NEXT r;
END LOOP;
END IF;
FOR r IN SELECT * FROM specie WHERE parent = $1
LOOP
RETURN NEXT r;
FOR r IN SELECT * FROM specie_children(r.specieid, FALSE)
LOOP
RETURN NEXT r;
END LOOP;
END LOOP;
RETURN;
END;
$BODY$
LANGUAGE plpgsql;
Call:
SELECT * FROM specie_children (1, true);
Returns:
specieid | parent
---------+-------
1 | 0
11 | 1
111 | 11
112 | 11
113 | 11
12 | 1
13 | 1
Related
i have created procedure, inside used cursor to update the some data, while calling the procedure it's getting the error.
create or REPLACE PROCEDURE bal_upd(p_id int) as
$$
DECLARE rc record;
----- cursor
bal_upd1 CURSOR (p_id int)
for
select * from tbal where custid = p_id;
begin
open bal_upd1 (p_id);
loop
FETCH bal_upd1 into rc;
exit when not found;
update t_trans set balance = balance + rc.trans;
COMMIT;
end loop;
close bal_upd1;
end;
$$ LANGUAGE plpgsql;
call bal_upd(1)
ERROR: cursor "bal_upd1" does not exist
CONTEXT: PL/pgSQL function bal_upd(integer) line 12 at FETCH
SQL state: 34000
create or REPLACE PROCEDURE bal_upd(p_id int) as
$$
DECLARE rc record;
----- cursor
bal_upd1 CURSOR (p_id int)
for
select * from tbal where custid = p_id;
begin
open bal_upd1 (p_id);
loop
FETCH bal_upd1 into rc;
exit when not found;
update t_trans set balance = balance + rc.trans;
COMMIT;
end loop;
close bal_upd1;
end;
$$ LANGUAGE plpgsql;
call bal_upd(1)
ERROR: cursor "bal_upd1" does not exist
CONTEXT: PL/pgSQL function bal_upd(integer) line 12 at FETCH
SQL state: 34000
You don't need a function or a loop for that:
UPDATE t_trans
SET balance = t_trans.balance + t.trans
FROM (SELECT sum(trans) AS trans
FROM tbal
GROUP BY custid) AS t
WHERE t_trans.custid = t.custid;
I tried, failed. I just found just use for loop (implicit cursor) is far more simple.
BEGIN;
CREATE temp TABLE tbal (
custid bigint
, trans numeric
);
INSERT INTO tbal VALUES (1 , 1);
INSERT INTO tbal VALUES (1 , 2);
CREATE temp TABLE t_trans (
custid bigint
, balance numeric
);
INSERT INTO t_trans VALUES (1 , 10);
COMMIT;
CREATE OR REPLACE PROCEDURE bal_upd (bigint)
AS $func$
DECLARE
rc record;
BEGIN
FOR rc IN
SELECT
*
FROM
tbal
WHERE
custid = $1 LOOP
RAISE NOTICE 'custid: %, trans: % ' , rc.custid , rc.trans;
UPDATE
t_trans ta
SET
balance = balance + (rc.trans)
WHERE
ta.custid = (rc.custid);
END LOOP;
END;
$func$
LANGUAGE plpgsql;
Then call it. CALL bal_upd(1);
I am trying to Create a cursor on cartesian product/join as below, it gives an error
create or replace function som1() returns integer as $$
declare
rCur cursor for (select* from t1);
er route%rowtype;
begin
for er in
select route_id, location, happy from t1, t2 where exams.pid = route.pid
loop
end loop;
return 4;
end;
$$ language plpgsql;
select som1();
I have a postgres function that I'd like to return the result of a query, but I'd like it to return nothing if that query matches more than 1 record.
So, something like:
CREATE OR REPLACE FUNCTION myFunc(_a text, _b text)
RETURNS yy
LANGUAGE plpgsql
STABLE
PARALLEL SAFE
AS $$
BEGIN
RETURN QUERY
SELECT *
FROM yy
WHERE a = x
AND b = y;
END;
$$;
Except, it should return nothing if that query matches more than 1 record.
CREATE OR REPLACE FUNCTION myFunc(_a text, _b text)
RETURNS SETOF yy -- To be able to return "nothing"
LANGUAGE plpgsql
STABLE
PARALLEL SAFE
AS $$
DECLARE
result yy;
BEGIN
SELECT *
INTO STRICT result -- STRICT allows to check that exactly one row returned
FROM yy
WHERE a = x
AND b = y;
RETURN NEXT result; -- RETURN NEXT - return yet another row for "RETURNS SETOF" function
EXCEPTION
WHEN no_data_found OR too_many_rows THEN -- When no data or more then one rows
RETURN; -- Nothing to return, just exit
END;
$$;
i guess this can help you out.
CREATE OR REPLACE FUNCTION database.myFunction(
IN text,IN text)
RETURNS TABLE(firstField, secondField, lastField) AS
$BODY$
--sql string is the variable containing the final sql code
declare sql_string text;
declare regs numeric;
begin
--this is what happens in case count<1
sql_string = 'select 0,0,0';
--now we count them
regs = (select count(firstField) from mytable where a=b)::numeric;
--if >=1, then whe get the whole data
if (regs>=1) then
sql_string = 'select firstField,secondField, lastField from mytable where a=b';
end if;
--and return to you...
return query EXECUTE sql_string;
end;
This is my first function:
CREATE FUNCTION func1(INOUT a integer,OUT b integer, OUT c float, OUT d integer,OUT e decimal)
AS $BODY$
DECLARE
BEGIN
.
.
.
END
$BODY$
LANGUAGE plpgsql VOLATILE
Output is:
$ SELECT * FROM func1(242);
a | b | c | d | e
--+--+--+--+---
242 | 5 | 7.5 | 15 | 1.2208
And this is second function:
CREATE FUNCTION func2()
AS $BODY$
DECLARE
d RECORD;
BEGIN
EXECUTE 'SELECT * FROM func1(242)' INTO d;
raise notice 'arr: %', d;. -– output is-> NOTICE: arr: (242,5,7.5,15,1.2208)
--HERE I want access to a,b,....
.
.
END
$BODY$
LANGUAGE plpgsql VOLATILE
How I can access separately to a,b,c,d and e variables inside func2.
Here's one solution:
FOR d in EXECUTE 'SELECT * FROM func1(242)'
LOOP
--here you can access them, or assign them to a local variable
raise notice 'a:%, b:%, c:%, d:%, e:%', d.a, d.b, d.c, d.d, d.d, d.e;
END LOOP;
I'd like to create a function for select and changed me data
CREATE OR REPLACE FUNCTION PublicatedTask( argument ) RETURNS SETOF task AS $$DECLARE
f task%ROWTYPE;
BEGIN
FOR f IN SELECT * FROM Task where layer IN $1 and publicationin<>0 ORDER BY id LOOP
if (f.publicationIN = 1) then
f.description='';
end if;
RETURN NEXT f;
END LOOP;
RETURN;
END;
$$
LANGUAGE 'plpgsql';
but I 'dont know what argument type?
I'd like to do SELECT * FROM PublicatedTask((1,2,3));
Thanks for your help
Or use VARIADIC:
CREATE OR REPLACE FUNCTION PublicatedTask( VARIADIC argument int[]) RETURNS SETOF task AS $$DECLARE
f task%ROWTYPE;
BEGIN
FOR f IN SELECT * FROM Task where layer = ANY($1) and publicationin<>0 ORDER BY id LOOP
if (f.publicationIN = 1) then
f.description='';
end if;
RETURN NEXT f;
END LOOP;
RETURN;
END;
$$
LANGUAGE 'plpgsql';
And use it this way:
SELECT * FROM PublicatedTask(1,2,3);
VARIADIC is available as of version 8.4: http://www.postgresql.org/docs/8.4/interactive/xfunc-sql.html#XFUNC-SQL-VARIADIC-FUNCTIONS
you could use an array of integers as parameter:
CREATE OR REPLACE FUNCTION PublicatedTask( argument int[]) RETURNS SETOF task AS $$DECLARE
f task%ROWTYPE;
BEGIN
FOR f IN SELECT * FROM Task where layer = ANY($1) and publicationin<>0 ORDER BY id LOOP
if (f.publicationIN = 1) then
f.description='';
end if;
RETURN NEXT f;
END LOOP;
RETURN;
END;
$$
LANGUAGE 'plpgsql';
Then you could call it this way:
SELECT * FROM PublicatedTask('{1,2,3}');