让函数返回表的更新部分
Posted
技术标签:
【中文标题】让函数返回表的更新部分【英文标题】:Having a function return an updated part of a table 【发布时间】:2019-08-29 07:25:18 【问题描述】:假设我有一个表 A。我想编写一个函数来创建一个临时表,其中包含 A 中与给定条件匹配的所有条目。然后,我想在不更新表 A 的情况下更新该临时表中的值,然后将临时表作为函数的结果返回。 我已经为函数的返回值定义了行和表类型。
现在的问题是如何在函数中创建、填充和更新临时表?
我偶然发现了 Common Table Expressions,它似乎可以满足我的要求,但我还了解到更新 CTE 也会更新它后面的表。
有什么想法吗?
【问题讨论】:
CTE(或在 Oracle 中,子查询分解)只是选择数据。它本身并没有更新(除非您在 UPDATE 或 MERGE 语句中使用它)。从您所说的来看,听起来您实际上并不需要将数据存储在临时表中,您只需要一个 select 语句来输出具有更新值的数据。获得这些数据后,您将如何处理? 刚刚读到这篇文章时想到,如果您使用的是Oracle 12c 或最新版本,您可以将数据转储为表格类型并将其用作表格。虽然它有一些限制,但可能会做你正在寻找的工作。 基本上我想做的是让函数替换给定列中的值。我做了一个严重依赖于所述列的计算,我希望能够比较如果该列中的值被其他值替换时的计算结果。你可能是对的,我可以用一个选择来做,我只是不知道怎么做,所以我试着用一个临时表来做。但是,Alex Pole 基本上回答了我的问题,我很好。 【参考方案1】:由于您已经定义了行和表类型,您可以使用 (pipelined) 集合返回类型。你还没有展示你的表格或类型,所以有一些简单的发明:
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
/
然后你可以有这样的功能:
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;
/
您不需要多个循环,但我已经这样做了以显示阶段。
如何在函数中创建、填充和更新临时表?
创建是通过使用您的表类型声明l_table
,并使用本地集合bulk collect into
填充。然后通过简单地为“行”类型中的字段分配新值来进行更新,这可以基于现有值或来自其他来源。在这里,我刚刚在集合中所有元素的第一个循环内增加了我的虚拟数据中的日期。
然后,为了将修改后的表数据返回给调用者,我使用了第二个循环,该循环将每一行输出。调用该函数然后返回:
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
您不必使用流水线函数,只需一次返回集合即可:
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
在调用者看来是一样的;不过,管道行可以更高效,并且更容易处理批量收集的限制。
db<>fiddle
有了这么简单的东西,您显然根本不需要函数,并且可以将原始表中的数据作为普通 SQL 语句的一部分进行操作(正如评论中提到的@Boneist)。您通常需要做一些相当复杂的事情才能使这种方法有价值。
【讨论】:
以上是关于让函数返回表的更新部分的主要内容,如果未能解决你的问题,请参考以下文章