【问题标题】:Simpler regular expression for oracle regexp_replaceoracle regexp_replace 的更简单的正则表达式
【发布时间】:2019-11-04 07:25:31
【问题描述】:

我有一个 | 分隔的字符串,其中包含 20 个 |s,例如 123|1|42|13||94123|2983191|2|98863|...|211| 最多 20 个 |。这是一个 oracle 数据库列。该字符串只有 20 个数字,后跟 |。

我试图从中取出一个字符串,在其中删除位置 4、6、8、9、11、12 和 13 处的数字。此外,需要将位置 16 处的数字移动到位置 4。直到现在,我有一个像

这样的正则表达式

select regexp_replace(col1, '^((\d*\|){4})(\d*\|)(\d*\|)(\d*\|)(\d*\|)((\d*\|){2})(\d*\|)((\d*\|){3})((\d*\|){2})(\d*\|)(.*)$', '\1|\4|\6||\9||||||||') as cc from table

这是我卡住的地方,因为 oracle 只支持最多 9 个组的反向引用。有没有办法让这个正则表达式更简单,所以它的组更少并且可以被替换?也欢迎任何替代解决方案/建议。

注意 - 位置计数器从 0 开始,所以上面字符串中的 123 是第 0 个数字。

编辑:示例 -

源字符串

|||14444|10107|227931|10115||10118||11361|11485||10110||11512|16666|||

预期结果

|||16666|10107||10115||||||11512||||

【问题讨论】:

  • 该表中是否还有其他列可以唯一标识每一行?
  • @Littlefoot - 是的,这里有一个 id 列
  • 您的预期结果似乎与第一个位置 0 不一致,因为在这种情况下,10107 实际上是位置 4 的数字,应该替换为 16666。

标签: regex oracle


【解决方案1】:

您可以通过删除您要从字符串中删除的数字的捕获组来获得所需的结果,并将(例如)((\d*\|){2}) 写为(\d*\|\d*\|)。这会将捕获组的数量减少到 7 个,从而使您的代码能够按原样工作:

select regexp_replace(col1, 
     '^(\d*\|\d*\|\d*\|\d*\|)\d*\|(\d*\|)\d*\|(\d*\|)\d*\|\d*\|(\d*\|)\d*\|\d*\|\d*\|(\d*\|\d*\|)(\d*\|)(.*)$', 
     '\1\6\2|\3||\4|||\5|\7') as cc 
from table

输出(用于您的测试数据以及@Littlefoot 好列示例):

CC
|||14444|16666|227931|||||11361|||||11512|||||
0|1|2|3|16|5||7|||10||||14|15||17|18|19|

Demo on dbfiddle

【讨论】:

  • 这正是我所追求的。谢谢
【解决方案2】:

由于有独特的列(ID,正如你所说),看看这是否有帮助:

  • 将每一列分成几行
  • 将它们组合回来(使用listagg),它使用2个CASEs:
    • 用于删除不需要的值
    • 另一个对它们进行正确排序(“将位置 16 的值放到位置 4”)

请注意,我的结果与您的不同;如果我数正确,16666 不在位置 16 而是 17 所以 - 11512 必须移动到位置 4。

我还添加了另一个 dummy 行,用于确认我是否正确计算位置,并说明为什么必须使用第 10-12 行(因为重复)。

好的,你来了:

SQL> with test (id, col) as
  2    (
  3    select 1, '|||14444|10107|227931|10115||10118||11361|11485||10110||11512|16666|||' from dual union all
  4    select 2, '1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20'                     from dual
  5    ),
  6  temp as
  7    (select replace(regexp_substr(col, '(.*?)(\||$)', 1, column_value), '|', '') val,
  8            column_value lvl,
  9            id
 10     from test cross join table(cast(multiset(select level from dual
 11                                              connect by level <= regexp_count(col, '\|') + 1
 12                                             ) as sys.odcinumberlist))
 13    )
 14  select id,
 15    listagg(case when lvl in (4, 6, 8, 9, 11, 12, 13) then '|'
 16                 else val || case when lvl = 20 then '' else '|' end
 17            end, '')
 18            within group (order by case when lvl = 16 then 4
 19                                        when lvl =  4 then 16
 20                                        else lvl
 21                                   end) result
 22  from temp
 23  group by id;

        ID RESULT
---------- ------------------------------------------------------------
         1 |||11512|10107||10115|||||||10110|||16666|||
         2 1|2|3|16|5||7|||10||||14|15||17|18|19|20

SQL>

【讨论】:

  • 这实际上是不正确的。第 4 列和第 5 列的值之间有一个额外的 |,而缺少第 16 列的值则没有 |
  • 真的,@Nick;谢谢你。已修复,需要更多输入和case 参与。
  • 感谢您的努力。尼克的答案更接近我正在做的事情,并且在我的情况下会更快,所以继续吧。干杯
  • 完全没问题。祝你好运!
猜你喜欢
  • 1970-01-01
  • 2019-06-28
  • 2018-07-19
  • 1970-01-01
  • 1970-01-01
  • 2012-11-15
  • 2016-01-17
  • 1970-01-01
  • 2010-12-05
相关资源
最近更新 更多