问题
I have a stored procedure, GetRegions, which fetches some columns into a cursor like so:
OPEN out_cur FOR
SELECT id, name, ...
FROM regions, ...
WHERE ...
This SP is used all over the place - in places where it will return tens of regions and in places where it will return tens of thousands.
I also have a function, GetRegionPath which gets the "path" of a region. It's a moderately expensive function - there's no problem running it on tens of regions, but on tens of thousands would be unacceptable.
I need to write a stored procedure, GetRegionsWithPaths, that gets regions with exactly the same logic, now and forever as GetRegions, but which includes the path of a region in the result set.
At the moment, GetRegionsWithPaths is an exact copy of GetRegions, with the addition of the path:
OPEN out_cur FOR
SELECT id, name, ..., GetRegionPath(id) path
FROM regions, ...
WHERE ...
but that's not acceptable - if someone edits GetRegions then the two SPs will be out of sync. What I'd like is to take the cursor from GetRegions and then add the path to it. Something like:
GetRegions(..., v_cur);
OPEN out_cur FOR
SELECT id, name, ..., GetRegionPath(id) path
FROM ( SELECT * FROM v_cur );
Is this possible? If so, what's the syntax?
回答1:
One possible solution (that could eventually be simplified) is to use a table function to process the cursor and to add the function value.
Assume the function returning the sys_refcursor is called get_cur and that the cursor consist of column ID, NAME (this is important, as the table function requires type definition).
You declare the TYPE for the row (including the additional path column) and for the resulting table.
create type t_row is object
( id number(10),
name varchar2(10),
path varchar2(10)
);
/
create type t_rows is table of t_row;
/
and define the table function fetching the cursor and adding the function call.
create or replace function get_cur2 return
t_rows
PIPELINED
as
cv_out sys_refcursor;
id number;
name varchar2(100);
begin
cv_out := get_cur;
loop
FETCH cv_out INTO id, name;
exit when cv_out%NOTFOUND;
pipe row(t_row(id,name, GetRegionPath(id)));
end loop;
close cv_out;
return;
end;
/
Now you may select the data from the table function
select * from table(get_cur2);
ID NAME PATH
---------- ---------- ----------
1 one path 1
2 two path 2
and of course you may use this query to open a cursor in a third function that will return the SYS_REFCURSOR with the additional path column.
回答2:
Alternative implementation using the parameter proposed in the comments.
Suppose the original function looks like this
create or replace function get_cur return SYS_REFCURSOR is
cv_out sys_refcursor;
begin
OPEN cv_out
FOR
select Id, name from tab;
return cv_out;
end;
/
You add a parameter return_path which is either 0 - which returns the original cursor or 1 - which will return the original curosr with the path column.
The implementation could be like this
create or replace function get_cur_with_param(return_path NUMBER) return SYS_REFCURSOR is
cv_out sys_refcursor;
v_stmt_str VARCHAR2(4000);
begin
v_stmt_str := 'select Id, name '||case when return_path = 1 then ', GetRegionPath(id)' end ||' from tab';
OPEN cv_out FOR v_stmt_str;
return cv_out;
end;
/
than get_cur_with_param(0) returns the 2 column cursor and get_cur_with_param(1) returns the cursor with the path column added.
Note that the SQL is build dynamically, but will end only with two variants, so this will be no problem for parsing. If you have additional parameters in the query use bind variables adding the USING clause.
来源:https://stackoverflow.com/questions/35485415/how-do-i-write-a-stored-procedure-which-adds-a-column-to-a-ref-cursor-from-anoth