具有函数 return table 的更新部分

Having a function return an updated part of a table

假设我有一个 table A。我想编写一个函数来创建一个临时 table,其中包含 A 中与给定条件匹配的所有条目。然后,我想更新临时 table 中的值而不更新 table A,然后 return 临时 table 作为函数的结果。 我已经为函数的 return 值定义了行和 table 类型。

现在的问题是如何在函数中创建、填充和更新临时 table?

我偶然发现了 Common Table 表达式,它似乎可以满足我的要求,但我还读到更新 CTE 也会更新它背后的 table。

有什么想法吗?

因为您已经定义了行和 table 类型,您可以使用 (pipelined) 集合 return 类型。你还没有用一些简单的发明来展示你的 table 或类型:

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;
/

你不需要多个循环,但我这样做是为了展示阶段。

how do I create, fill and update a temporary table in a function?

通过使用您的 table 类型声明 l_table 来创建,并使用 bulk collect into 本地集合进行填充。然后通过简单地将新值分配给 'row' 类型中的字段进行更新,这些值可以基于现有值或来自其他来源。在这里,我刚刚在集合中所有元素的第一个循环内增加了我的虚拟数据中的日期。

然后 return 将修改后的 table 数据发送给调用者,我使用了第二个循环将每一行输出。然后调用函数 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

您不必使用流水线函数,您可以 return 一次性收集:

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

对于这种微不足道的事情,您显然根本不需要函数,只需将原始 table 中的数据作为普通 SQL 语句的一部分进行操作即可(如@Boneist 所述在评论中)。您通常需要做一些非常复杂的事情才能使这种方法有价值。