Reputation: 535
Lets say I have a table A. I want to write a function that creates a temporary table that contains all entries from A that match a given condition. Then, I want to update values in that temporary table without updating table A, and then return the temporary table as the result of the function. I already have defined row and table types for the return value of my function.
Now the question is how do I create, fill and update a temporary table in a function?
I have stumbled upon Common Table Expressions which seem to do what I want but I have also read that updating an CTE also updates the table behind it.
Any ideas?
Upvotes: 0
Views: 374
Reputation: 191425
As you already have row and table types defined, you can use a (pipelined) collection return type. You haven't shown your table or types so with some simple invented ones:
create table a (col1, col2, col3) as
select 1, 'First', date '2019-01-01' from dual
union all
select 2, 'Second', date '2019-01-01' from dual
union all
select 3, 'Third', date '2019-01-01' from dual;
create type t_row as object (
x varchar2(10),
y date
)
/
create type t_table as table of t_row
/
you can then have a function like this:
create or replace function foo
return t_table pipelined as
l_table t_table;
begin
select t_row(col2, col3)
bulk collect into l_table
from a
where col1 > 1;
for i in l_table.first..l_table.last loop
-- do any updates you need
l_table(i).y := l_table(i).y + i * interval '1' day;
end loop;
-- do any thing else you need
for i in l_table.first..l_table.last loop
-- return modified data
pipe row (l_table(i));
end loop;
return;
end;
/
You don't need multiple loops but I've done it like that to show the stages.
how do I create, fill and update a temporary table in a function?
Creation is by declaring l_table
using your table type, and filling is with bulk collect into
that local collection. Updating is then by simply assigning new values to fields in your 'row' type, which can be based on exiting values or from some other source. Here I've just incremented the dates in my dummy data, inside the first loop over all the elements in the collection.
Then to return the modified table data to the caller, I've used a second loop which pipes each row out. Calling the function then returns:
select * from table(foo);
X Y
---------- ----------
Second 2019-01-02
Third 2019-01-03
The original table is unmodified:
select * from a;
COL1 COL2 COL3
---------- ------ ----------
1 First 2019-01-01
2 Second 2019-01-01
3 Third 2019-01-01
You don't have to use a pipelined function, you can just return the collection in one go:
create or replace function foo
return t_table as
l_table t_table;
begin
select t_row(col2, col3)
bulk collect into l_table
from a
where col1 > 1;
for i in l_table.first..l_table.last loop
-- do any updates you need
l_table(i).y := l_table(i).y + i * interval '1' day;
end loop;
-- do any thing else you need
return l_table;
end;
/
select * from table(foo);
X Y
---------- ----------
Second 2019-01-02
Third 2019-01-03
which looks the same to the caller; piping rows can be more efficient though, and makes it easier to work with bulk collect's limit.
With something this trivial you obviously don't need a function at all, and can just manipulate the data in your original table as part of a plain SQL statement (as @Boneist mentioned in a comment). You generally need to be doing something quite complicated to make this sort of approach worthwhile.
Upvotes: 1