Если бы я вам и моим основным фактором было выступление я хотел бы использовать функции таблицы. Решение mathguys работает отлично, но оно будет более эффективным, если мы будем использовать конвейерную функцию.
Сначала мы создаем типы, необходимые для нашей функции.
drop type type_test_table;
drop type type_test_row;
CREATE TYPE type_test_row AS OBJECT (
code varchar2(2000),
descr VARCHAR2(50)
)
/
CREATE TYPE type_test_table IS TABLE OF type_test_row
/
Затем мы создаем нашу функцию:
create or replace function test_pipe_func return type_test_table pipelined as
cursor c_data_in is
select '12361'||level||'_BBMS_GTECHL'||level||'|12362'||level||'_BBMS_PRIM'||level||'|12363'||level||'_BBMS_SEC'||level||'|12364'||level||'_BBU_SEC'||level as str from dual
connect by level <= 1000000;
v_element varchar2(300);
v_code varchar2(100);
v_descr varchar2(200);
p_deb number;
p_fin number;
begin
for l_data_in in c_data_in loop
p_deb := 0;
p_fin := 1;
while p_fin > 0 loop
p_fin := case when p_deb = 0 then instr(l_data_in.str,'|',1, 1) else instr(l_data_in.str,'|',p_deb-1, 2) end;
p_deb := case when p_deb = 0 then 1 else instr(l_data_in.str,'|',p_deb-1, 1)+1 end;
v_element := case when p_fin = 0 then substr(l_data_in.str, p_deb) else substr(l_data_in.str, p_deb, p_fin - p_deb) end;
p_deb := p_fin +1;
v_code := substr(v_element, 1 , instr(v_element, '_' , 1,1)-1);
v_descr := substr(v_element, instr(v_element, '_' , 1,1)+1);
pipe row(type_test_row(v_code, v_descr));
end loop;
end loop;
end test_pipe_func;
/
я изменил тестовый пример немного, чтобы быть в состоянии генерировать столько строк, сколько необходимо для моих тестов. И я использовал конвейерную функцию для ограничения использования памяти процесса в случае больших наборов данных и возможности использовать его с помощью select. Если ваш вариант использования отличается (я не знаю, может быть, вставить в таблицу с помощью ввода), другой вариант может состоять в том, чтобы использовать сборку в объеме и forall.
create or replace procedure test_bulk_collect_proc as
cursor c_data_in is
select '12361'||level||'_BBMS_GTECHL'||level||'|12362'||level||'_BBMS_PRIM'||level||'|12363'||level||'_BBMS_SEC'||level as str from dual
connect by level <= 1000000;
type type_table_data_in is table of c_data_in%rowtype;
table_data_in type_table_data_in;
v_element varchar2(300);
v_code varchar2(100);
v_descr varchar2(200);
p_deb number;
p_fin number;
v_str varchar2(4000);
v_t_insr type_test_table;
limit_in number := 100000;
i number;
begin
OPEN c_data_in;
LOOP
FETCH c_data_in BULK COLLECT INTO table_data_in LIMIT limit_in;
v_t_insr := type_test_table();
i := 1;
for indx IN 1 .. table_data_in.COUNT LOOP
v_str := table_data_in(indx).str;
p_deb := 0;
p_fin := 1;
while p_fin > 0 loop
p_fin := case when p_deb = 0 then instr(v_str,'|',1, 1) else instr(v_str,'|',p_deb-1, 2) end;
p_deb := case when p_deb = 0 then 1 else instr(v_str,'|',p_deb-1, 1)+1 end;
v_element := case when p_fin = 0 then substr(v_str, p_deb) else substr(v_str, p_deb, p_fin - p_deb) end;
p_deb := p_fin +1;
v_code := substr(v_element, 1 , instr(v_element, '_' , 1,1)-1);
v_descr := substr(v_element, instr(v_element, '_' , 1,1)+1);
v_t_insr.extend;
v_t_insr(i) := type_test_row(v_code, v_descr);
i:= i+1;
end loop;
END LOOP;
forall t in v_t_insr.first..v_t_insr.last
insert into test_bbu(CODE, DESCR) values (v_t_insr(t).code, v_t_insr(t).descr);
EXIT WHEN table_data_in.COUNT < limit_in;
END LOOP;
End;
/
Я проверил все три метода в моей базе данных. Чтобы протестировать sql mathguy и конвейерную функцию, я использовал CTAS и для массового сбора в i просто выполнил процедуру.
create table test_bbu as
with input_data (input_str) as (
select '12361'||level||'_BBMS_GTECHL'||level||'|12362'||level||'_BBMS_PRIM'||level||'|12363'||level||'_BBMS_SEC'||level from dual
connect by level <= 1000000
),
t (str) as (
select '|' || input_str || '|' from input_data
),
r (lvl, code, descr, str, p1_from, p2_from, p1_to, p2_to) as (
select 0, null, null, str, 1, 1, instr(str, '_', 1, 1), instr(str, '|', 1, 2)
from t
union all
select lvl+1, substr(str, p2_from + 1, p1_to - p2_from - 1),
substr(str, p1_to + 1, p2_to - p1_to - 1),
str, p1_to, p2_to, instr(str, '_', p2_to + 1, 1),
instr(str, '|', p2_to + 1, 1)
from r
where p1_to != 0
)
select code, descr
from r
where lvl != 0;
create table test_bbu2 as
select * from table(test_pipe_func);
execute test_bulk_collect_proc;
Я проверил три метода с линиями 500K и 1M. Вот мои результаты, но я призываю вас проверить свою окружающую среду, прежде чем принимать решение.
500K 1M
----------------------------------------
SQL 36s 1m:15s
Pipelined 11s 23s
Bulk Collect 8s 17s
Возможный дубликат [это] (http://stackoverflow.com/questions/14328621/splitting-string-into-multiple-rows-in-oracle) – Aleksej
Не совсем дубликат - Расщепление труба, разделенных строка действительно является дубликатом, но в этой задаче есть два токена на строку, разделенную на канал, разделенные в свою очередь первым подчеркиванием (где может быть больше символов подчеркивания). – mathguy