我应该清除每个要在 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 甲骨文的主要内容,如果未能解决你的问题,请参考以下文章