Это упрощенная опция, которая работает с одной строкой из исходной таблицы; если он должен работать для нескольких строк, все усложняется (не то, что это невозможно сделать, но этот код будет выглядеть даже хуже ).
Также предполагается, что каждая «подстрока» (строка) может быть преобразована в VARCHAR2
(с использованием TO_CHAR
).
Пример данных:
SQL> select * from test;
COL
--------------------------------------------------------------------------------
BlahBlah Blah;
TextAboutSomethingToFind.ExampleA;
ExtraText; MoreExtraText;EvenMoreExtraText;
LastExtraText;
TextAboutSomethingToFind.ExcibitB; TextAboutSomethingToFind.Pattern?;
EndText;
Решение, написанное по-моему . Следуйте за комментариями в коде. Если вы не уверены в том, что он делает, запустите его CTE за CTE и посмотрите значения, которые возвращает каждый из этих SELECT
s.
SQL> with temp2 as
2 -- split multi-lines column to rows, separated by CHR(10)
3 (select level lvl2,
4 to_char(regexp_substr(col, '[^' || chr(10) ||']+', 1, level, 'm')) col2
5 from test
6 connect by level <= regexp_count(col, chr(10)) + 1
7 ),
8 temp3 as
9 -- out of all rows from the previous step, split those - that contain "SomethingToFind"
10 -- more than once - into their own separate rows
11 (select lvl2,
12 level lvl3,
13 trim(to_char(regexp_substr(col2, '[^;]+', 1, level))) col3
14 from (select lvl2,
15 col2 from temp2
16 where instr(col2, 'SomethingToFind') > 0
17 and regexp_count(col2, ';') > 1
18 )
19 connect by level <= regexp_count(col2, ';')
20 ),
21 almost_there as
22 -- apply the "Caution" clause to all "SomethingToFinds" from both cases
23 (select lvl2,
24 to_number(null) lvl3,
25 case when instr(col2, 'SomethingToFind') > 0 then
26 '/* Caution' ||chr(10) || col2 || chr(10) || 'Caution */'
27 else col2
28 end col
29 from temp2
30 where lvl2 not in (select lvl2 from temp3)
31 union all
32 select lvl2,
33 lvl3,
34 case when instr(col3, 'SomethingToFind') > 0 then
35 '/* Caution' ||chr(10) || col3 || chr(10) || 'Caution */'
36 else col3
37 end col
38 from temp3
39 )
40 -- Finally, put them together using LISTAGG, separated by CHR(10)
41 select listagg(col, chr(10)) within group (order by lvl2, lvl3) result
42 from almost_there;
RESULT
--------------------------------------------------------------------------------
BlahBlah Blah;
/* Caution
TextAboutSomethingToFind.ExampleA;
Caution */
ExtraText; MoreExtraText;EvenMoreExtraText;
LastExtraText;
/* Caution
TextAboutSomethingToFind.ExcibitB
Caution */
/* Caution
TextAboutSomethingToFind.Pattern?
Caution */
EndText;
SQL>