Oracle CLOB REGEXP_REPLACE建议

时间:2019-01-12 03:57:59

标签: sql regex oracle oracle11g clob

我的CLOB文本类似于:

A:123, A:983, A:122, B:232, B:392, C:921, D:221, D:121, D:838

我希望得到类似

的结果
A:123, 983, 122, B:232, 392, C:921, D:221, 121, 838

请注意,

  • 海量数据,肯定超过4000个字符。
  • 字符可以重复。
  • 数字始终是唯一的。最多也可以输入11位数字。
  • 不得修改原始表数据。
  • 结果不应该排序

这看起来很简单,我们只需要删除CLOB文本中的重复项即可。我无法在SQL中设计逻辑,有人可以建议吗?

更新: 我已经找到了通过自定义Java程序进行迭代和删除重复项的解决方案。这对我来说可以。仍然好奇地看到SQL方法。

2 个答案:

答案 0 :(得分:0)

您可以将regexp_substrinstrregexp_count结合使用:

select listagg(letter||':'||nr,',') within group (order by letter)
       as "Result String"
  from
  (
  select letter, listagg(nr,',') within group (order by letter) as nr
    from
    (
    with t(str) as
    (
     select 'A:123, A:983, A:122, B:232, B:392, C:921, D:221, D:121, D:838' from dual
    )
    select substr(regexp_substr(str,'[^:]+',instr(str,':'),level),1, instr(str,',')
                                                                    -instr(str,':')-1) nr,     
           substr(str,instr(str,':',1,level)-1,1) letter      
      from t
    connect by level < regexp_count(str,'[^:]+')
    ) 
   group by letter  
   );

Result String
-------------------------------------------
A:122,123,983,B:232,392,C:921,D:121,221,838

P.S。您需要to_char转换为Clob列(即col_clob)。替换

select 'A:123, A:983, A:122, B:232, B:392, C:921, D:221, D:121, D:838' from dual

使用

select to_char(col_clob) from your_table

Rextester Demo

答案 1 :(得分:0)

此查询解析字符串(搜索冒号),并返回冒号前面的aech字母的字符串出现 second 的位置:

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开始的两个空格。

将CL B:替换为CLOB中从位置29开始的两个空格。

请注意,我要替换为两个空格以不改变字符串中的位置,但这可以轻松增强并替换为NULL。

因此,基本思想是保持前pos-1个字符相同,并replace保留字符串的其余部分,最后CONCAT修饰这两个部分:

 concat(substr(txt,1, pos-1) , replace( substr(txt, pos), str, '  '));

我在一个函数中实现了整个logik,该函数返回更改后的CLOB,因此可以 在查询和更新语句中都使用了

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实施要好得多)。一种可能的调优方法是在函数中使用其他logik来识别短字符串并将其作为VARCHAR字符串进行处理。