2012-03-26 24 views
1

我有一個表叫RULE_TABLE其中有列RULE_SEG1RULE_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_SEG1RULE_SEG2必須按照????的方式進行擴展,那麼它必須從0000擴展到9999;如果它的484?,那麼它必須從4840擴大到4849;如果它的02*那麼它必須從02000擴展到02999.從RULE_SEG1和RULE_SEG2生成的連接值將被插入MY_TABLE。此外,只有當函數FV_SEGMENT_DESCRIPTION返回的值等於'COST_CENTRE'或'NATURAL_ACCOUNT'時,纔會分別將RULE_SEG1RULE_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_CENTRENATURAL_ACCOUNT_TABLE中的NATURAL_ACCOUNT有索引。該程序需要11.16小時才能在開發實例中運行。數據庫是Oracle 11g企業版。請提出想法來調整代碼。解釋計劃除了瓶頸可能是由於CONNECT BY LEVEL造成的情況外並沒有多大幫助。

事後 後分析插入MY_TABLE數據的時間戳,我發現的最大時間是以下兩個案例:

案例1RULE_SEG1????並且它必須是從0000擴大到9999 案例2RULE_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值,然後展開。請建議!

+0

任何想法的朋友? – Arcs 2012-03-28 16:56:32

回答

1

我認爲我們沒有足夠的信息來真正做出有關性能的判斷。 我們會需要解釋的計劃和autotraces和這種真正挖掘這就是說,這裏是我的瘋狂建議:

與價值觀「0000」到「9999」和「00000」到「99999創建一個實際的表'消除 connect by level部分。如果您認爲這是限制因素,請將其除掉。創建一個表格 ALL_ACCOUNTS以及您的查詢的預期結果。它只有110000個4-5個字符串的記錄。 絕對TINY。

然後,簡化你的邏輯。你的代碼看起來非常程序化,這可能會影響性能。 您可以在一個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 
    ) 
;