У меня есть таблица с именем 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 должен быть расширен.
Вот фрагмент кода, который вызывает серьезные проблемы с производительностью в 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 Enterprise Edition.Пожалуйста, предложите идеи для настройки кода.Объяснить план не очень помогает, за исключением того факта, что горлышко бутылки, вероятно, связано с УСТАНОВКОЙ УРОВНЯ.
ПОСЛЕ ТОГО, КАК После анализа метки времени данных, вставленных в 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
, а затем расширялся.Пожалуйста, предложите !!