2016-05-16 2 views
0

У меня есть строка, которая выглядит как -Извлечение значения из трубы разделены строки

12361_BBMS_GTECHL|12362_BBMS_PRIM|12363_BBMS_SEC|....and so on 

Так что я должен принести

12361 and BBMS_GTECHL 
12362 and BBMS_PRIM 
12363 and BBMS_SEC 

я использовал -

select * 
    FROM 
    TABLE(XMLSEQUENCE(
     EXTRACT(
      XMLTYPE('<rowset><row><Code>'|| 
       replace(replace('12361=BBMS_GTECHL|12362=BBMS_PRIM','|','</Value></row><row><Code>'),'=','</Code><Value>')||'</Value>'||'</row></rowset>'),'/rowset/row'))); 


declare 
    l_val varchar2(1000); 
begin 
    select substr('12361_BBMS_GTECHL|12362_BBMS_PRIM', instr('|')+1) into  l_val from dual; 
    dbms_output.put_line(l_val); 
end; 

Но получение проблемы в получении желаемого результата! Мне нужно написать эту логику в пакете, который я буду делать, если у меня есть какой-то намек.

Моя DB версия -

Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production 
+0

Возможный дубликат [это] (http://stackoverflow.com/questions/14328621/splitting-string-into-multiple-rows-in-oracle) – Aleksej

+0

Не совсем дубликат - Расщепление труба, разделенных строка действительно является дубликатом, но в этой задаче есть два токена на строку, разделенную на канал, разделенные в свою очередь первым подчеркиванием (где может быть больше символов подчеркивания). – mathguy

ответ

2

Вот решение, используя рекурсивную разложенном подзапрос ("рекурсивное CTE"). Обратите внимание на использование указателей на расположение символов трубы и первое подчеркивание после каждого канала (без учета других подчеркиваний). Кроме того, решение использует только стандартные INSTR и SUBSTR, избегая использования регулярных выражений (которые работают несколько медленнее - важно, если вы обрабатываете большое количество данных).

with input_data (input_str) as (
      select '12361_BBMS_GTECHL|12362_BBMS_PRIM|12363_BBMS_SEC' from dual 
    ), 
    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; 

Выход:

CODE DESCR 
------- -------------------- 
12361 BBMS_GTECHL 
12362 BBMS_PRIM 
12363 BBMS_SEC 
+0

Спасибо Mathguy, ваш запрос работает отлично. Мне нужно использовать эту логику в моем пакете, где я обрабатываю много данных для переноса. Я хочу знать, могу ли я достичь этого, используя синтаксический анализ XML, который, как я считаю, быстрее. – mradul

0

Если бы я вам и моим основным фактором было выступление я хотел бы использовать функции таблицы. Решение 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 
Смежные вопросы