我应该清除每个要在 forall 中使用的 fetch 循环的集合吗? PLSQL 甲骨文

Posted

技术标签:

【中文标题】我应该清除每个要在 forall 中使用的 fetch 循环的集合吗? PLSQL 甲骨文【英文标题】:Should I clear collections every fetch loop to be used in forall? PLSQL Oracle 【发布时间】:2020-04-12 06:40:21 【问题描述】:

假设我在 table1 中有大量数据(100k-1m 行),我需要进行一些检查,然后根据过滤结果更新它们在 table2 上的状态。 正如您在我的简化代码中看到的那样。我每批批量收集 1000 行并将它们过滤到 3 个不同的集合(dsa1、dsa2、dsa3)中,然后再将这 3 个集合更新到 table2 中。

我的问题是,假设第一次提取将 100 行放入 dsa1,然后第二次提取仅将 70 行放入 dsa1。当 forall 更新运行时,它还会从第一次 fetch 更新 dsa1 中的旧 30 行。

2 我想到的解决方案是首先删除收集每个 fetch 循环中的所有元素。第二个是将 forall 放在 fetch 循环之外,这将使 3 个集合非常大,但 forall 只调用一次。

第二种解决方案会占用大量内存对吗?请建议最好的解决方案是什么

declare

cursor c1 is 
select t1.id, t1.status, t2.con from table1 t1, table2 t2
where t1.id = t2.id;

type ty_c1 is table of c1%rowtype;
asd1 ty_c1 := ty_c1();

type ty_id is table of c1.id%type index by pls_integer;
dsa1 ty_id;
dsa2 ty_id;
dsa3 ty_id;
begin
    open c1;
    loop
        fetch c1 bulk collect into asd1 limit 1000;
        exit when asd1.count = 0;

        for i in 1 .. asd1.count
        loop
            if (asd1(i).status = 'ACT') then
                dsa1(i).id := asd1(i).id;
            elsif (asd1(i).status = 'NOT ACT') then
                dsa2(i).id := asd1(i).id;
            else
                dsa3(i).id := asd1(i).id;
            end if;
        end loop;

        forall idx in indices of dsa1
            update table2 set con = 'ACTIVE'
            where id = dsa1(idx).id;

        forall idx in indices of dsa2
            update table2 set con = 'NOT ACTIVE'
            where id = dsa2(idx).id;

        forall idx in indices of dsa3
            update table2 set con = 'DEAD'
            where id = dsa3(idx).id;


    end loop;
    close c1;
end;

【问题讨论】:

【参考方案1】:

从性能的角度来看,构建三个集合并只访问一次数据库会提高性能,但会消耗更多内存。

所以答案取决于您将要处理的典型卷和可用内存。

您还可以拥有第四个保持为空的集合,并将空集合分配给循环内的 dsa1、dsa2 和 dsa3,而不是删除条目。

但是看看你的代码,因为只有状态在改变,为什么不根据项目和状态的记录创建一个集合,或者有第二个集合来保存状态,然后不管状态如何,你将更新 1000 条记录一个集合的时间。

更进一步,因为 1M 记录对于 oracle 数据库来说不算什么,(数十亿记录是巨大的,一百万记录是微不足道的)只需使用数据库执行单个更新 select 语句

update table2 t2
set conn=(select decode(t1.status, 'ACT', 'ACTIVE', 'NOT ACT','NON ACTIVE','DEAD') 
from table1 t1)
where t2.id=t1.id

t2 中不存在于 t1 中的 NB 行将 conn 设置为 null,但如果不需要,您可以限制 where 子句以限制影响。

您还可以在更新和选择上添加并行提示以使用更多 CPU,如果您能够修改数据模型,使用数字代码而不是 varchar 来表示状态也会更有效。

【讨论】:

感谢您的回复。根据我的检查,我忘记提及(或在我的问题中没有明确提及)。如果我使用 1 个集合,我可能需要更新两个表或仅 1 个表(表 1 或表 2),那么每次获取将是 1000,但它会在不需要时使用相同的数据更新另一个表被更新。如果我使用 2 个集合(1 个用于表 1,另一个用于表 2),那么它将再次获取先前提取的数据。

以上是关于我应该清除每个要在 forall 中使用的 fetch 循环的集合吗? PLSQL 甲骨文的主要内容,如果未能解决你的问题,请参考以下文章

Forall 语句适用于 PDDL 域中的元素子集?

使用 PL/SQL 使用 forall 语法批量插入

字典上的 PostScript forall

自动清除和重新填充文本区域值

forall 作为这些集合的交集

如何清除#ngrx 中的状态?