Ich habe eine Tabelle namens RULE_TABLE
die Spalten RULE_SEG1
und RULE_SEG2
Performance-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
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!!
Irgendwelche Ideen Freunde? – Arcs