Oracle - 替换第一次出现的多个值

时间:2016-12-20 13:00:59

标签: sql oracle oracle10g

我想替换字符串中第一次出现的多个值。请考虑以下示例:

输入字符串: a,a,a,b,b,c

要替换的值: a,b

预期输出: ,a,a,,b,c

使用|运算符不起作用,如下所示:

select regexp_replace('a,a,a,b,b,c','a|b',null,1,1) from dual;

输出:,a,a,b,b,c。这仅替换a的第一次出现。

作为对此的扩展,如果要多次替换a,那么第二次出现也应该在输入中替换:

输入字符串a,a,a,b,b,c

要替换的值: a,b,a

预期输出: ,,a,,b,c

这可以通过REGEXP_REPLACE或其他方式实现吗?如果有,怎么样?在此先感谢您的帮助!

3 个答案:

答案 0 :(得分:0)

我猜可能是更好的解决方案。

我只能用递归SQL =( 查看下面的脚本:

$NF != ""

答案 1 :(得分:0)

这是完全通用性的一种方法。如果我正确理解了问题,给定逗号分隔的字符串,请替换稍后在字符串中使用NULL重复的每个符号(标记) - 但保留所有逗号作为占位符。该解决方案涵盖了原始字符串已经具有NULL的占位符的情况(即,连续逗号之间没有标记的连续逗号)。在极端情况下,原始字符串也可能为空(与Oracle中的NULL相同)。

问题的唯一其他合理含义是“符号”或“标记”只有在它们立即跟随相同的符号时才应被删除。也就是说,a,a,a,b,b,a,a,b,b,b之类的输入应该变为,,a,,b,,a,,,b。在第一个解释中,它应该成为,,,,,,a,,,b。下面的解决方案实现了“第一解释”(在修改后的字符串中只剩下一个a和一个b); OP的问题不清楚是否需要哪种解释。如果需要“第二种解释”,可以修改下面的解决方案(使用基本的“Tabibitosan方法”来识别连续的相同值)。

解决方案分割每个字符串 - 仔细,因为可能有NULL个标记,然后它标识每个不同符号的最后一个匹配项,替换所有空标记和所有不是“最后一组”的标记占位符符号(这里我用'〜' - 它应该是输入字符串中未使用的符号),然后用LISTAGG()聚合所有内容并删除所有占位符符号(~)。需要占位符是因为LISTAGG()从输入中删除了NULL(Oracle开发人员做出了令人遗憾的选择)。

该解决方案使用正则表达式;如果性能非常重要,则可以避免使用它们(使用INSTRSUBSTR) - 代价是使代码更复杂。

with
-- begin test data definition (do not include in final query)
     test_data as (
       select 1 as id, 'a,a,a,b,b,c' as str from dual union all
       select 2      , ''                   from dual union all
       select 3      , 'a,b,a,b,b,a'        from dual union all
       select 4      , 'x,y,z,z,z'          from dual union all
       select 5      , 'a,a,a,a'            from dual union all
       select 6      , 'a,b,c,c,a'          from dual union all
       select 7      , 'x'                  from dual union all
       select 8      , 'p,q,r'              from dual union all
       select 9      , 'a,b,,,a,c'          from dual
     ),
-- end of test data; solution (query) continues below this line
     prep as (
       select id, str, level as lvl,
              regexp_substr(str, '([^,]*)(,|$)', 1, level, null, 1) as token
       from   test_data
       connect by level <= regexp_count(str, ',') + 1
              and prior id = id
              and prior sys_guid() is not null
     ),
     with_rn as (
       select id, str, lvl, token,
              row_number() over (partition by id, token order by lvl desc) as rn
       from   prep              
     )
select id, str, 
       translate( listagg ( case when token is null or rn > 1 then '~'
                                 else token end, ','
                          ) within group (order by lvl), 'x~', 'x'
                ) as modif_str
from  with_rn
group by id, str
;

<强>输出

 ID STR            MODIF_STR
--- -------------- --------------
  1 a,a,a,b,b,c    ,,a,,b,c
  2
  3 a,b,a,b,b,a    ,,,,b,a
  4 x,y,z,z,z      x,y,,,z
  5 a,a,a,a        ,,,a
  6 a,b,c,c,a      ,b,,c,a
  7 x              x
  8 p,q,r          p,q,r
  9 a,b,,,a,c      ,b,,,a,c

9 rows selected.

答案 2 :(得分:-1)

一种解决方案是使用两个regexp_replace() s:

select regexp_replace(regexp_replace('a,a,a,b,b,c', 'a', null, 1, 1), 'b', null, 1, 1)
from dual;

我不确定是否存在使用单个正则表达式的(合理)解决方案。