2012-03-26 7 views
1

Ich habe eine Tabelle namens RULE_TABLE die Spalten RULE_SEG1 und RULE_SEG2Performance-Tuning mit CONNECT BY LEVEL in Abfrage

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 

Jede Regel in der hat RULE_SEG1 und RULE_SEG2 muss in der Art erweitert werden, wenn es ???? dann von 0000 bis 9999 erweitert werden muss; wenn es 484? ist, dann muss es von 4840 bis 4849 erweitert werden; wenn es 02* ist, dann muss es von 02000 zu 02999 erweitert werden. Der verkettete Wert, der von RULE_SEG1 und RULE_SEG2 generiert wird, soll in MY_TABLE eingefügt werden. Auch die von RULE_SEG1 und RULE_SEG2 generierten Werte müssen nur dann mit Werten in COST_CENTRE Tabelle und NATURAL_ACCOUNT Tabelle verglichen werden, wenn der von der Funktion FV_SEGMENT_DESCRIPTION zurückgegebene Wert gleich 'COST_CENTRE' oder 'NATURAL_ACCOUNT' ist. Functon FN_SEGMENT_LENGTH gibt die Länge zurück, bis zu der RULE_SEG1/RULE_SEG2 erweitert werden muss.

Hier ist das Code-Snippet, das schwerwiegende Leistungsprobleme in Oracle 11g verursacht.

 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 hat 9800 Zeilen, COST_CENTRE_TABLE hatte rund 230 Zeilen. NATURAL_ACCOUNT_TABLE hat 936 Zeilen. Die Gesamtzahl der in MY_TABLE einzufügenden Zeilen beträgt 220000. Es gibt einen Index für COST_CENTRE in COST_CENTRE_TABLE und NATURAL_ACCOUNT in NATURAL_ACCOUNT_TABLE. Das Programm benötigt 11.16 Stunden, um in der Entwicklungsinstanz ausgeführt zu werden. Datenbank ist Oracle 11g Enterprise Edition. Bitte schlagen Sie Ideen vor, um den Code abzustimmen. Erklären Plan ist nicht viel Hilfe, außer für die Tatsache, dass der Flaschenhals wahrscheinlich auf CONNECT BY LEVEL zurückzuführen ist.

nachträgliche Nach der Analyse fand den Zeitstempel von Daten, eingefügt in MY_TABLE, ich heraus, dass die maximale Zeit, für den folgenden beide Fall genommen ist:

Fall 1 wenn RULE_SEG1 ist ???? und es hat zu sein erweitert 0000-9999 Fall 2 wenn RULE_SEG2* ist, und es hat 00.000-99.999

erweitert werden

Diese Schleife erweitert die RULE_SEG1 und prüft, ob die resultierenden Werte in COST_CENTRE_TABLE existieren (wenn lv_seg_desc1 = 'COST_CENTRE'). Gibt es eine Möglichkeit, die CONNECT BY LEVEL Abfrage so zu gestalten, dass sie zuerst mit den COST_CENTRE Werten prüft und dann expandiert. Bitte vorschlagen!!

+0

Irgendwelche Ideen Freunde? – Arcs

Antwort

1

Ich glaube nicht, dass wir hier genug Informationen haben, um wirklich über die Leistung zu urteilen. Wir müssten erklären, Pläne und Autotraces und solche wirklich zu graben. Das heißt, hier ist mein verrückter Vorschlag:

Erstellen Sie eine aktuelle Tabelle mit den Werten '0000' bis '9999' und '00000' bis '99999 'den connect by level Teil zu beseitigen. Wenn Sie denken, dass dies der limitierende Faktor ist, dann sollten Sie es loswerden.Erstellen Sie eine Tabelle ALL_ACCOUNTS mit den erwarteten Ergebnissen aus Ihrer Abfrage. Es sind nur 110000 Datensätze von 4-5 Zeichenfolgen. Absolut TINY.

Dann vereinfachen Sie Ihre Logik. Ihr Code sieht sehr prozedural aus, was sich auf die Leistung auswirken kann. Sie könnten wahrscheinlich das meiste davon in einem SQL machen. Ich habe nicht eine Datenbank praktisch zum Herumtollen mit, aber diese als Richtschnur dienen könnte:

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 
    ) 
; 
Verwandte Themen