我有一個表叫RULE_TABLE
其中有列RULE_SEG1
和RULE_SEG2
與CONNECT性能調優由地級查詢
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
。此外,只有當函數FV_SEGMENT_DESCRIPTION
返回的值等於'COST_CENTRE'或'NATURAL_ACCOUNT'時,纔會分別將RULE_SEG1
和RULE_SEG2
生成的值與COST_CENTRE
表和NATURAL_ACCOUNT
表中的值分別進行比較。 Functon FN_SEGMENT_LENGTH
返回值,直到需要擴展RULE_SEG1/RULE_SEG2。
以下是導致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
和NATURAL_ACCOUNT_TABLE
中的NATURAL_ACCOUNT
有索引。該程序需要11.16小時才能在開發實例中運行。數據庫是Oracle 11g企業版。請提出想法來調整代碼。解釋計劃除了瓶頸可能是由於CONNECT BY LEVEL造成的情況外並沒有多大幫助。
事後 後分析插入MY_TABLE
數據的時間戳,我發現的最大時間是以下兩個案例:
案例1時RULE_SEG1
是????
並且它必須是從0000擴大到9999 案例2時RULE_SEG2
爲*
,它必須從00000擴展至99999
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
值,然後展開。請建議!
任何想法的朋友? – Arcs 2012-03-28 16:56:32