问题
I am trying to write a loop using PL/pgSQL (PostgreSQL 9.3) function that returns a table. I used RETURN NEXT; with no parameters after each query in the loop, following examples found like plpgsql error "RETURN NEXT cannot have a parameter in function with OUT parameters" in table-returning function, and elsewhere. However, I am still getting an error that says:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
A minimal code example to reproduce the problem is below. Can anyone please help explain how to fix the test code to return a table?
Thanks in advance.
Minimal example:
CREATE OR REPLACE FUNCTION test0()
RETURNS TABLE(y integer, result text) AS $func$
DECLARE
yr RECORD;
BEGIN
FOR yr IN SELECT * FROM generate_series(1,10,1) AS y_(y)
LOOP
RAISE NOTICE 'Computing %', yr.y;
SELECT yr.y, 'hi';
RETURN NEXT;
END LOOP;
RETURN;
END
$func$ LANGUAGE plpgsql;
回答1:
The example given may be wholly replaced with RETURN QUERY:
BEGIN
RETURN QUERY SELECT y_.y, 'hi' FROM generate_series(1,10,1) AS y_(y)
END;
which will be a lot faster.
In general you should avoid iteration wherever possible, and instead favour set-oriented operations.
Where return next over a loop is unavoidable (which is very rare, and mostly confined to when you need exception handling) you must set OUT parameter values or table parameters, then return next without arguments.
In this case your problem is the line SELECT yr.y, 'hi'; which does nothing. You're assuming that the implicit destination of a SELECT is the out parameters, but that's not the case. You'd have to use the out parameters as loop variables like @peterm did, use assignments or use SELECT INTO:
FOR yr IN SELECT * FROM generate_series(1,10,1) AS y_(y)
LOOP
RAISE NOTICE 'Computing %', yr.y;
y := yr.y;
result := 'hi';
RETURN NEXT;
END LOOP;
RETURN;
回答2:
One way to do it
CREATE OR REPLACE FUNCTION test0()
RETURNS TABLE(y integer, result text) AS $$
BEGIN
FOR y, result IN
SELECT s.y, 'hi' result FROM generate_series(1,10,1) AS s(y)
LOOP
RETURN NEXT;
END LOOP;
END
$$ LANGUAGE plpgsql;
SELECT * FROM test0();
Outcome:
| Y | RESULT | |----|--------| | 1 | hi | | 2 | hi | | 3 | hi | | 4 | hi | | 5 | hi | | 6 | hi | | 7 | hi | | 8 | hi | | 9 | hi | | 10 | hi |
Here is a SQLFiddle demo
回答3:
What @Craig already explained.
Plus, if you really need a loop, you can have this simpler / cheaper. You don't need to declare an additional record variable and assign repeatedly. Assignments are comparatively expensive in plpgsql. Assign to the OUT variables declared in RETURNS TABLE directly. Those are visible everywhere in the code and the FOR loop can also assign to a list of variables. Per documentation:
The target is a record variable, row variable, or comma-separated list of scalar variables.
CREATE OR REPLACE FUNCTION test0()
RETURNS TABLE(y integer, result text) AS
$func$
DECLARE
yr RECORD;
BEGIN
FOR y, result IN
SELECT g, 'text_'::text || g
FROM generate_series(1,10) g
LOOP
RAISE NOTICE 'Computing %', y;
RETURN NEXT;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Additional points
Do not use the identifier
ytwice (asOUTparam and as column alias) while you can easily avoid it. That's a loaded footgun. Sometimes this can't be avoided, always table-qualify columns in this case.The final
RETURNwithout params is good form, but totally optional. When control reaches the finalEND, the complete result is returned automatically.ginFROM generate_series(1,10) gis both table alias and column alias automatically, unless an explicit column alias is given. It is effectively the same asFROM generate_series(1,10) g(g).
来源:https://stackoverflow.com/questions/27663197/how-to-use-return-nextin-pl-pgsql-correctly