Этот запрос анализирует строку (в поисках двоеточий) и возвращает позицию второго вхождения строки для буквы aech, предшествующей двоеточию:
with col as
(select 'A:123, A:983, A:122, B:232, B:392, C:921, D:221, D:121, D:838' col from dual),
t1 as(
select col, instr(col,':',1,level)-1 pos
from col
connect by level <= length(col) - length(replace(col,':',null))
),
t2 as (
select to_char(substr(col,pos,2)) str,
pos
from t1),
t3 as (
select
str, pos,
row_number() over (partition by str order by pos) rn
from t2)
select
str, pos
from t3
where rn = 2
;
По сути, вы разделяете строку для каждого двоеточия (я использую подход length(replace
для повышения производительности по сравнению с регулярным выражением), извлекая подстроку X: и ее положение.
Чем использовать row_number()
для получения второго вхождения partition by str
.
Обратите внимание, что он работает с CLOB любой длины, потому что только строка длиной 2 преобразуется в VARCHAR.
Результат
STR POS
-------- ----------
A: 8
B: 29
D: 50
Интерпретация
заменить A:
двумя пробелами в CLOB, начиная с позиции 8.
заменить B:
двумя пробелами в CLOB, начиная с позиции 29.
и т.д.
Обратите внимание, что я заменяю двумя пробелами, чтобы не менять позиции в строке, но это можно легко улучшить и заменить на NULL.
Таким образом, основная идея состоит в том, чтобы сохранить первые символы pos-1 одинаковыми, а replace
остальную часть строки и, наконец, CONCAT
ввести обе части:
concat(substr(txt,1, pos-1) , replace( substr(txt, pos), str, ' '));
Я реализовал весь логик в функции, которая возвращает измененный CLOB, так что это может быть
используется как в запросах, так и в выражении update:
select id, col, upd_clob(col) from tc;
update tc
set col = upd_clob(col);
Код функции
create or replace function upd_clob(txt CLOB) return CLOB
as
v_txt CLOB := txt;
begin
for r_upd in (
with dt as
(select txt from dual),
t1 as(
select txt, instr(txt,':',1,level)-1 pos
from dt
connect by level <= length(txt) - length(replace(txt,':',null))
),
t2 as (
select to_char(substr(txt,pos,2)) str,
pos
from t1),
t3 as (
select
str, pos,
row_number() over (partition by str order by pos) rn
from t2)
select
str, pos
from t3
where rn = 2)
loop
v_txt := concat(substr(v_txt,1, r_upd.pos-1) , replace( substr(v_txt, r_upd.pos), r_upd.str, ' '));
end loop;
return(v_txt);
end;
/
Вы можете посчитать производительность для большого CLOB не очень хорошей (хотя IMO намного лучше, чем альтернативная реализация REGEXP). Возможным подходом к настройке будет дополнительный логик в функции, которая распознает короткие строки и обрабатывает их как строки VARCHAR.