我应该清除每个获取循环中的集合以用于 forall 吗? PLSQL甲骨文
Should I clear collections every fetch loop to be used in forall? PLSQL Oracle
假设我在 table1 中有大量数据(100k-1m 行),我需要进行一些检查,然后根据过滤结果更新它们在 table2 上的状态。
正如您在我的简化代码中看到的那样。我每批批量收集 1000 行,并将它们过滤到 3 个不同的集合(dsa1、dsa2、dsa3)中,然后将这 3 个集合全部更新到 table2 中。
我的问题是,假设第一次提取将 100 行放入 dsa1,然后第二次提取仅将 70 行放入 dsa1。当 forall 更新运行时,它还会从第一次获取更新 dsa1 中的旧 30 行。
2 我想到的第一个解决方案是删除收集每个获取循环中的所有元素。其次是将 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;
从性能的角度来看,构建三个集合并只访问数据库一次将以使用更多内存为代价提高性能。
所以答案取决于您将处理的典型卷和可用内存。
您还可以拥有第四个保持为空的集合,并将空集合分配给循环内的 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 设置为空,但如果不需要,您可以限制 where 子句以限制影响。
您还可以在更新和 select 上添加并行提示以使用更多 CPU,如果您能够修改数据模型,则使用数字代码而不是 varchar 来表示状态也会更有效率。
假设我在 table1 中有大量数据(100k-1m 行),我需要进行一些检查,然后根据过滤结果更新它们在 table2 上的状态。 正如您在我的简化代码中看到的那样。我每批批量收集 1000 行,并将它们过滤到 3 个不同的集合(dsa1、dsa2、dsa3)中,然后将这 3 个集合全部更新到 table2 中。
我的问题是,假设第一次提取将 100 行放入 dsa1,然后第二次提取仅将 70 行放入 dsa1。当 forall 更新运行时,它还会从第一次获取更新 dsa1 中的旧 30 行。
2 我想到的第一个解决方案是删除收集每个获取循环中的所有元素。其次是将 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;
从性能的角度来看,构建三个集合并只访问数据库一次将以使用更多内存为代价提高性能。
所以答案取决于您将处理的典型卷和可用内存。
您还可以拥有第四个保持为空的集合,并将空集合分配给循环内的 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 设置为空,但如果不需要,您可以限制 where 子句以限制影响。
您还可以在更新和 select 上添加并行提示以使用更多 CPU,如果您能够修改数据模型,则使用数字代码而不是 varchar 来表示状态也会更有效率。