我有一个名为RULE_TABLE
的表格,其中包含RULE_SEG1
列和RULE_SEG2
RULE_SEG1 | RULE_SEG2
-----------------------
???? | 0100?
0200 | 02*
484? | ????
COST_CENTRE_TABLE
COST_CENTRE
-----------
0000
0100
0199
0200
4841
4842
4842
NATURAL_ACCOUNT_TABLE
NATURAL_ACCOUNT
---------------
01001
01002
01005
01009
02001
02334
02611
12345
12347
12378
19999
RULE_SEG1
和RULE_SEG2
中的每个规则必须以????
的方式进行扩展,然后必须从0000扩展到9999;如果它是484?
,那么它必须从4840扩展到4849;如果它的02*
则必须从02000扩展到02999.从RULE_SEG1和RULE_SEG2生成的连接值将被插入MY_TABLE
。此外,RULE_SEG1
和RULE_SEG2
生成的值必须分别与COST_CENTRE
表和NATURAL_ACCOUNT
表中的值进行比较,前提是函数FV_SEGMENT_DESCRIPTION
返回的值是等于'COST_CENTRE'或'NATURAL_ACCOUNT'。 Functon FN_SEGMENT_LENGTH
返回必须扩展RULE_SEG1 / RULE_SEG2的lenth。
以下是导致Oracle 11g中出现严重性能问题的代码段。
for rec_rule in (select rule_seg1, rule_seg2 from rule_table) loop
ln_seg1_len number := fn_segment_length(rule_seg1);
ln_seg2_len number := fn_segment_length(rule_seg2);
ln_seg1_len_power number := power(10, ln_seg1_len);
ln_seg2_len_power number := power(10, ln_seg2_len);
lv_seg_desc1 varchar2(100) := fv_segment_description(rule_seg1);
lv_seg_desc2 varchar2(100) := fv_segment_description(rule_seg2);
begin
for rec_1 in (select b.num seg1
from (select a.num
from (select lpad(level - 1, ln_seg1_len, '0') as num
from dual
connect by level <= ln_seg1_len_power
) a
where a.num like replace(rec_rule.rule_seg1, '?', '_')) b
where ((lv_seg_desc1 = 'COST_CENTRE' and exists
(select 1
from cost_centre_tbl c
where c.cost_centre = b.num
and rownum = 1)) or
(lv_seg_desc1 = 'NATURAL_ACCOUNT' and exists
(select 1
from natural_account_tbl n
where n.natural_account = b.num
and rownum = 1)) or
(lv_seg_desc1 <> 'COST_CENTRE' and
lv_seg_desc1 <> 'NATURAL_ACCOUNT'))) loop
if lv_seg2 is not null then
for rec_2 in (select b.num seg2
from (select a.num
from (select lpad(level - 1, ln_seg2_len, '0') as num
from dual
connect by level <= ln_seg2_len_power
) a
where a.num like
replace(replace(rec_rule.rule_seg2, '?', '_'),
'*',
'%')) b
where ((lv_seg_desc2 = 'COST_CENTRE' and exists
(select 1
from cost_centre_tbl c
where c.cost_centre = b.num
and rownum = 1)) or
(lv_seg_desc2 = 'NATURAL_ACCOUNT' and exists
(select 1
from natural_account_tbl n
where n.natural_account = b.num
and rownum = 1)) or
(lv_seg_desc2 <> 'COST_CENTRE' and
lv_seg_desc3 <> 'NATURAL_ACCOUNT'))) loop
lv_sourcekey := rec_1.seg1 || rec_2.seg2;
ltab_map_level_2(l_cntr_level_2).sourcekey := lv_sourcekey;
l_cntr_level_2 := l_cntr_level_2 + 1;
end loop; -- rec_2
end if;
end loop;
forall j in l_cntr_level_2 .first .. l_cntr_level_2 .last
-- insert into staging table
insert into my_table
values
(my_table_s.nextval,
ltab_map_level_2 (j).sourcekey,
);
exception
when others then
dbms_output.put_line(sqlerrm);
end loop;
RULE_TABLE
有9800行,COST_CENTRE_TABLE
有大约230行。 NATURAL_ACCOUNT_TABLE
有936行。要插入MY_TABLE
的总行数为220000. COST_CENTRE
中的COST_CENTRE_TABLE
和NATURAL_ACCOUNT
中的NATURAL_ACCOUNT_TABLE
都有索引。该程序在开发实例中运行需要11.16小时。数据库是Oracle 11g企业版。请提出调整代码的建议。解释计划没有多大帮助,除了瓶颈可能是由于CONNECT BY LEVEL。
事后
在分析插入MY_TABLE
的数据的时间戳后,我发现最多花费的时间是以下两种情况:
RULE_SEG1
为????
并且必须从0000扩展到9999时 案例1
RULE_SEG2
为*
并且必须从00000扩展到99999 案例2
for rec_1 in (select b.num seg1
from (select a.num
from (select lpad(level - 1, ln_seg1_len, '0') as num
from dual
connect by level <= ln_seg1_len_power
) a
where a.num like replace(rec_rule.rule_seg1, '?', '_')) b
where ((lv_seg_desc1 = 'COST_CENTRE' and exists
(select 1
from cost_centre_tbl c
where c.cost_centre = b.num
and rownum = 1)) or
(lv_seg_desc1 = 'NATURAL_ACCOUNT' and exists
(select 1
from natural_account_tbl n
where n.natural_account = b.num
and rownum = 1)) or
(lv_seg_desc1 <> 'COST_CENTRE' and
lv_seg_desc1 <> 'NATURAL_ACCOUNT')))
此循环展开RULE_SEG1
并检查COST_CENTRE_TABLE
中是否存在结果值(如果lv_seg_desc1 ='COST_CENTRE')。有没有办法设计CONNECT BY LEVEL
查询,以便首先检查COST_CENTRE
值,然后展开。请建议!!
答案 0 :(得分:1)
我认为我们在这里没有足够的信息来真正判断性能。 我们需要解释计划和autotraces等真正深入挖掘。那就是说,这是我疯狂的建议:
创建一个实际的表,其值为'0000'到'9999'和'00000'到'99999'以消除
connect by level
部分。如果您认为这是限制因素,请摆脱它。创建一个表
ALL_ACCOUNTS包含查询的预期结果。它只有110000条4-5个字符串的记录。
绝对有点。
然后,简化你的逻辑。您的代码看起来非常程序化,这可能会影响性能。 您可以在一个SQL中完成大部分操作。我没有一个方便的数据库,但这个 可以作为指导:
insert into my_table( id, sourcekey )
select
my_table_s.nextval,
a.num seg1 || b.num seg2 as lv_sourcekey
from
(select num from ALL_ACCOUNTS where num like translate(rec_rule.rule_seg1, '?*', '_%')) a,
(select num from ALL_ACCOUNTS where num like translate(rec_rule.rule_seg2, '?*', '_%')) b
where
exists ( -- Conditions for seg1
select 1
from (
select 'COST_CENTRE' as seg_desc,
cost_centre as acct
from cost_centre_tbl
union
select 'NATURAL_ACCOUNT' as seg_desc,
natural_account as acct
from natural_account_tbl ) comb1
where lv_seg_desc1 = comb1.seg_desc and
a.num = comb1.acct
)
AND exists ( -- Conditions for seg2
select 1
from (
select 'COST_CENTRE' as seg_desc,
cost_centre as acct
from cost_centre_tbl
union
select 'NATURAL_ACCOUNT' as seg_desc,
natural_account as acct
from natural_account_tbl ) comb1
where lv_seg_desc1 = comb1.seg_desc and
a.num = comb1.acct
)
;