2016-01-30 3 views
0

Я получаю текст оракула, заключенного между разделителями. Если возможно, пожалуйста, помогите в создании Regex для текста. Я пример текстаИзвлечение текста между разделителем с использованием регулярного выражения в оракуле

12322ABCD124A||!!123!!word1 !!word2!! word3!!||!!789!!word4!!word5 !! word6!!||!!2345 !!word7!!word8!! 890!!|| 

До сих пор я только в состоянии принести:

||!!123!!word1 !!word2!! word3!!||!!789!!word4!!word5 !! word6!!||!!2345 !!word7!!word8!! 890!! 

с помощью этого (\|\|(.*))+([^\|\|]).

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

массив [1] = (123, слово1, слово2, слово3)

массив [2] = (789, word4, word5, word6)

массива [3] = (2345, word7, word8, 890)

+0

Попробуйте использовать ** REGEXP_REPLACE ** и сопоставьте шаблон. –

ответ

1

Это один должен работать:

with v1 as 
(
    select '12322ABCD124A||!!123!!word1 !!word2!! word3!!||!!789!!word4!!word5 !! word6!!||!!2345 !!word7!!word8!! 890!!||' t from dual 
) 
select level -1 id, trim(',' from regexp_replace(regexp_substr(t,'[^\|]+',1,level),'!!',',')) array from v1 
where level > 1 
connect by level <= regexp_count(t,'\|\|'); 

Выход:

 ID ARRAY 
---------- -------------------------- 
     1 123,word1 ,word2, word3 
     2 789,word4,word5 , word6 
     3 2345 ,word7,word8, 890 

И если количество частей постоянной (4), и Вы хотите их в отдельных колонках:

with v1 as 
(
     select '12322ABCD124A||!!123!!word1 !!word2!! word3!!||!!789!!word4!!word5 !! word6!!||!!2345 !!word7!!word8!! 890!!||' t from dual 
    ), v2 as 
    (
    select level -1 id, trim(',' from regexp_replace(regexp_substr(t,'[^\|]+',1,level),'!!',',')) array 
    from v1 
    where level > 1 
    connect by level <= regexp_count(t,'\|\|') 
    ) 
    select id, 
    regexp_substr(array,'[^,]+',1,1) val1, 
    regexp_substr(array,'[^,]+',1,2) val2, 
    regexp_substr(array,'[^,]+',1,3) val3, 
    regexp_substr(array,'[^,]+',1,4) val4 
    from v2; 

Выход:

 ID VAL1  VAL2  VAL3  VAL4 
---------- ---------- ---------- ---------- ---------- 
     1 123  word1  word2  word3 
     2 789  word4  word5  word6 
     3 2345  word7  word8  890 

PLSQL STYLE:

declare 
    type t_text_array is table of varchar2(4000); 
    v_text_array t_text_array := t_text_array(); 
    val varchar2(4000); 
    cursor c1 is 
    select '12322ABCD124A||!!123!!word1 !!word2!! word3!!||!!789!!word4!!word5 !! word6!!||!!2345 !!word7!!word8!! 890!!||' t from dual; 
begin 
    open c1; 
    fetch c1 bulk collect into v_text_array; 
    for i in 1..v_text_array.count loop 
    for j in 2..regexp_count(v_text_array(i),'\|\|') loop 
     val := trim(',' from regexp_replace(regexp_substr(v_text_array(i),'[^\|]+',1,j),'!!',',')); 
     for k in 1..regexp_count(val,',')+1 loop 
     --display to console or further process... 
     dbms_output.put_line(regexp_substr(val,'[^,]+',1,k)); 
     end loop; 
    end loop; 
    end loop; 
end; 
/
+0

Спасибо за предоставление решения. Но мы не можем считать его постоянным (4). Я приведу только пример. В моем коде он будет увеличиваться до более чем 50 значений. Но формат остается таким же. Если возможно, попробуйте сделать его общим, например, проверить счетчик, а затем выполнить заранее. – Tanul

+0

более 50 отдельных значений (столбцов) для каждой строки? –

+0

Да. Значит, мы не знаем количество исправлений. На производственных серверах мы должны создать только общий код. Отсутствие жесткого кодирования – Tanul

0

Нижеприведенные один возвращает ожидаемые результаты:

with x as 
(select '2322ABCD124A||!!123!!word1 !!word2!! word3!!||!!789!!word4!!word5 !! word6!!||!!2345 !!word7!!word8!! 890!!||' str 
from dual), 
y as (
select regexp_substr(str,'[^||]+[!!]*', 1, level) str from x 
    where level > 1 
    connect by regexp_substr(str, '[^||]+[!!]*', 1, level) is not null 
    ) 
select 
    regexp_replace (
     regexp_replace (
     regexp_replace(str, '^!!', '(') , 
      '!!$', ')'), 
      '[ ]*!![ ]*', ',') str 
    from y 
+0

Еще одно, это возможно для того, чтобы каждый раз превышать каждое значение. Я хочу, чтобы что-то вроде выполнения цикла for до подсчета полных строк. Затем из каждой строки выбирайте каждое значение и назначайте переменную и обрабатывайте ее. Затем 2-я строка и присвойте все 4 значения тем же переменным и обработайте их и так далее ... – Tanul

+0

Я пробовал код с циклом for. например для цикла I в (ваш запрос) dbms_output.put_line (I.STR); контур конца; Но STR возвращается подряд за строкой. Есть ли что-то вроде STR [1] для доступа к 123 и т. Д. – Tanul

+0

Можете ли вы отобразить 4 значения в 4 столбца. Потому что общее количество значений в каждой строке останется таким же. В этом случае также будет легко получить доступ к каждому значению – Tanul

0

Вам нужно применять два раза раскол ограничителем, как описано here. Наконец, снова получите значения (слово), используя LISTAGG, и завершите их конкатенацией.

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

Возможно, вам потребуется настроить таблицу T2, ограничивающую количество разделов. Некоторая специальная обработка также необходима, если вы можете иметь значения NULL в ключевом слове.

Запрос - прокомментировал ниже

WITH t1 AS 
    (SELECT 1 id, 
    '12322ABCD124A||!!123!!word1 !!word2!! word3!!||!!789!!word4!!word5 !! word6!!||!!2345 !!word7!!word8!! 890!!|| ' col 
    FROM dual 
    UNION ALL 
    SELECT 2 id, 
    '22222ACCCC12Y||!!567!!word21 !!word22!! word23!!||!!789!!word24!!word25 !! word26!!||!!2345 !!word27!!word28!! 890!!|| ' col 
    FROM dual 
), 
    t2 AS 
    (SELECT rownum colnum 
    FROM dual 
    CONNECT BY level < 10 
    /* (max) number of columns */ 
), 
    t3 AS 
    (SELECT t1.id, 
    t2.colnum, 
    regexp_substr(t1.col,'[^|]+', 1, t2.colnum) col 
    FROM t1, 
    t2 
    WHERE regexp_substr(t1.col, '[^|]+', 1, t2.colnum) IS NOT NULL 
), 
    first_split AS 
    (SELECT id, colnum, col FROM t3 WHERE col LIKE '%!!%' 
), 
    second_split AS 
    (SELECT t1.id, 
    t1.colnum linenum, 
    t2.colnum, 
    regexp_substr(t1.col,'[^!]+', 1, t2.colnum) col 
    FROM first_split t1, 
    t2 
    WHERE regexp_substr(t1.col, '[^!]+', 1, t2.colnum) IS NOT NULL 
), 
    agg_values AS 
    (SELECT id, 
    linenum, 
    LISTAGG(col, ',') WITHIN GROUP (
    ORDER BY colnum) val_lst 
    FROM second_split 
    GROUP BY id, 
    linenum 
) 
SELECT id, 
    'array[' 
    || row_number() over (partition BY ID order by linenum) 
    || ']= (' 
    ||val_lst 
    ||')' array_text 
FROM agg_values 
ORDER BY 1,2 

Урожайность по запросу

ID ARRAY_TEXT 
    1 array[1]= (123, word1, word2, word3) 
    1 array[2]= (789, word4, word5, word6)  
    1 array[3]= (2345, word7, word8, 890)   
    2 array[1]= (567, word21, word22, word23)   
    2 array[2]= (789, word24, word25, word26)   
    2 array[3]= (2345, word27, word28, 890) 

Это результат first_split запроса. Вы разбиваете данные в строках.

ID  COLNUM COL 
---------- ---------- ------------------------------------------ 
1   2 !!123!!word1 !!word2!! word3!! 
1   3 !!789!!word4!!word5 !! word6!! 
1   4 !!2345 !!word7!!word8!! 890!! 
2   2 !!567!!word21 !!word22!! word23!! 
2   3 !!789!!word24!!word25 !! word26!! 
2   4 !!2345 !!word27!!word28!! 890!! 

second_split запросов разбивает строки в слове.

ID LINENUM  COLNUM COL 
---------- ---------- ---------- -------------------------------------------------------------------------------------------------------------------------- 
1   2   1 123 
1   2   2 word1 
1   2   3 word2 
1   2   4 word3 
1   3   1 789 
1   3   2 word4 
1   3   3 word5 
..... 

Остальное LISTAGG, чтобы получить список ключевых слов в формате CSV и функция ROW_NUMBER, чтобы получить хорошие последовательные array_ids

Если вы хотите, чтобы извлечь значения в отдельных столбцах используйте PIVOT вместо LISTAGG. Недостатком является то, что вы должны настроить запрос на фактическое количество значений.

WITH t1 AS 
    (SELECT 1 id, 
    '12322ABCD124A||!!123!!word1 !!word2!! word3!!||!!789!!word4!!word5 !! word6!!||!!2345 !!word7!!word8!! 890!!|| ' col 
    FROM dual 
    UNION ALL 
    SELECT 2 id, 
    '22222ACCCC12Y||!!567!!word21 !!word22!! word23!!||!!789!!word24!!word25 !! word26!!||!!2345 !!word27!!word28!! 890!!|| ' col 
    FROM dual 
), 
    t2 AS 
    (SELECT rownum colnum 
    FROM dual 
    CONNECT BY level < 10 
    /* (max) number of columns */ 
), 
    t3 AS 
    (SELECT t1.id, 
    t2.colnum, 
    regexp_substr(t1.col,'[^|]+', 1, t2.colnum) col 
    FROM t1, 
    t2 
    WHERE regexp_substr(t1.col, '[^|]+', 1, t2.colnum) IS NOT NULL 
), 
    first_split AS 
    (SELECT id, colnum, col FROM t3 WHERE col LIKE '%!!%' 
), 
    --select * from first_split order by 1,2,3; 
    second_split AS 
    (SELECT t1.id, 
    t1.colnum linenum, 
    t2.colnum, 
    regexp_substr(t1.col,'[^!]+', 1, t2.colnum) col 
    FROM first_split t1, 
    t2 
    WHERE regexp_substr(t1.col, '[^!]+', 1, t2.colnum) IS NOT NULL 
), 
    pivot_values AS 
    (SELECT * 
    FROM second_split PIVOT (MAX(col) col FOR (colnum) IN (1 AS "K1", 2 AS "K2", 3 AS "K3", 4 AS "K4")) 
) 
SELECT id, 
    row_number() over (partition BY ID order by linenum) AS array_id, 
    K1_COL, 
    K2_COL, 
    K3_COL, 
    K4_COL 
FROM pivot_values 
ORDER BY 1,2; 

дает мнение о реляционная

ID ARRAY_ID K1_COL K2_COL K3_COL K4_COL 
---------- ---------- -------- -------- -------- -------- 
1   1 123  word1 word2  word3 
1   2 789  word4 word5  word6 
1   3 2345  word7 word8  890 
2   1 567  word21 word22 word23 
2   2 789  word24 word25 word26 
2   3 2345  word27 word28 890 
+0

В вашем случае также удаляются пробелы .. Например, например, вместо слова «word1» это давало слово «word1». I – Tanul

+0

это возможно, чтобы каждый из них мог различать отдельно. Я хочу, чтобы что-то вроде выполнения для цикла до количества массивов. Затем из каждого массива выбирайте каждое значение и назначайте переменную типа 123 на переменную1, word1 на переменную2 и обрабатывайте ее. Затем 2-й массив и присвойте все 4 значения одним и тем же переменным и обработайте его и так далее ... – Tanul

+0

Вы можете отобразить 4 значения в 4 столбца. Потому что общее количество значений в каждой строке останется таким же. В этом случае также будет легко получить доступ к каждому значению – Tanul

0

Oracle Setup:

CREATE TABLE table_name (id, value) AS 
SELECT 1, '12322ABCD124A||!!123!!word1 !!word2!! word3!!||!!789!!word4!!word5 !! word6!!||!!2345 !!word7!!word8!! 890!!||' FROM DUAL UNION ALL 
SELECT 2, '12322ABCD124A||!!321!!word1a !!word2a!! word3a!!||!!987!!word4a!!word5a !! word6a!!||!!5432 !!word7a!!word8a!! 098!!||' FROM DUAL; 

Query 1:

SELECT id, 
     grp_no, 
     CAST(
     MULTISET(
      SELECT REGEXP_SUBSTR(t.text, '!\s*([^!]+?)\s*!', 1, LEVEL, NULL, 1) 
      FROM DUAL 
      CONNECT BY LEVEL <= REGEXP_COUNT(t.text, '!\s*([^!]+?)\s*!') 
     ) 
     AS SYS.ODCIVARCHAR2LIST 
     ) AS words 
FROM (
    SELECT id, 
      COLUMN_VALUE AS grp_no, 
      REGEXP_SUBSTR(value, '\|([^|]+)\|', 1, COLUMN_VALUE, NULL, 1) AS text 
    FROM table_name t, 
      TABLE(
      CAST(
       MULTISET(
       SELECT LEVEL 
       FROM DUAL 
       CONNECT BY LEVEL <= REGEXP_COUNT(t.value, '\|([^|]+)\|') 
      ) 
       AS SYS.ODCINUMBERLIST 
      ) 
     ) 
) t; 

Результаты:

 ID  GRP_NO WORDS 
---------- ---------- -------------------------------------------------------- 
     1   1 SYS.ODCIVARCHAR2LIST('123','word1','word2','word3') 
     1   2 SYS.ODCIVARCHAR2LIST('789','word4','word5','word6') 
     1   3 SYS.ODCIVARCHAR2LIST('2345','word7','word8','890') 
     2   1 SYS.ODCIVARCHAR2LIST('321','word1a','word2a','word3a') 
     2   2 SYS.ODCIVARCHAR2LIST('987','word4a','word5a','word6a') 
     2   3 SYS.ODCIVARCHAR2LIST('5432','word7a','word8a','098') 

Запрос 2:

SELECT id, 
     grp_no, 
     REGEXP_SUBSTR(t.text, '!\s*([^!]+)!', 1, 1, NULL, 1) AS Word1, 
     REGEXP_SUBSTR(t.text, '!\s*([^!]+)!', 1, 2, NULL, 1) AS Word2, 
     REGEXP_SUBSTR(t.text, '!\s*([^!]+)!', 1, 3, NULL, 1) AS Word3, 
     REGEXP_SUBSTR(t.text, '!\s*([^!]+)!', 1, 4, NULL, 1) AS Word4 
FROM (
    SELECT id, 
      COLUMN_VALUE AS grp_no, 
      REGEXP_SUBSTR(value, '\|([^|]+)\|', 1, COLUMN_VALUE, NULL, 1) AS text 
    FROM table_name t, 
      TABLE(
      CAST(
       MULTISET(
       SELECT LEVEL 
       FROM DUAL 
       CONNECT BY LEVEL <= REGEXP_COUNT(t.value, '\|([^|]+)\|') 
      ) 
       AS SYS.ODCINUMBERLIST 
      ) 
     ) 
) t; 

Результаты:

ID GRP_NO WORD1 WORD2 WORD3 WORD4 
---- ------ ------- ------- ------- ------- 
    1  1 123  word1 word2 word3   
    1  2 789  word4 word5 word6 
    1  3 2345 word7 word8 890  
    2  1 321  word1a word2a word3a 
    2  2 987  word4a word5a word6a 
    2  3 5432 word7a word8a 098