Oracle:regexp处理一个复杂的案例

时间:2018-12-29 06:08:01

标签: sql regex oracle

我有一张桌子,其中一列包含一个字符串,其中各项之间用分号(;)隔开

我想根据字符串的模式有选择地将数据传输到新表中。

例如,它可能看起来像

  

16 ;; 14; 30; 24; 11; 13; 14; 14; 10; 13; 18; 15; 18; 24; 13/18; 11 ;; 23; 12 ;; 19; 10 ;; 11 ; 26 ;;; 42; 26; 38/39; 12 ;;;;;;; 11 ;;;;;;;;;;

  

11 ;; 11; 11; 11; 11; 11; 11; 11; 11; 11; 11; 11; 11; 11; 11; 11; 11 ;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;

我不在乎分号之间是什么,但是我在乎哪些位置包含项目。例如,如果我只希望第一,第三,第四位置包含项目,则允许以下内容...

  

32 ;; 14; 18/12 ;;;;;;;;;或32 ;; 14; 18/12 ;;;; 55 ;;;; 11 ;;;;;;;

下面的那一个不好,因为第三位置没有任何值。

  

32 ;;; 18/12 ;;;;;;;;

如果正则表达式可以解决这个问题,那么我可以使用merge into将所需的记录移至目标表。如果无法做到这一点,我将不得不处理Java中的每条记录,然后有选择地将记录插入新表中。

源表:

id | StringValue | count

目标表:

id | StringValue | count

我要记住的sql:

merge into you_target_table tt
using ( select StringValue, count
        from source_table where REGEXP_LIKE ( StringValue, 'some pattern')
      ) st
on ( st.StringValue = tt.StringValue and st.count=tt.count )
when not matched then
   insert (id, StringValue , count) 
   values (someseq.nextval, st.value1, st.count)
when matched then
   update
   set tt.count = tt.count + st.count;

我还可以肯定源表中的所有StringValue都是唯一的,因此when matched then之后的内容并不重要,但是由于语法的原因,我认为我必须有一些东西。

2 个答案:

答案 0 :(得分:3)

对于每个位置,您需要一个值[^;]+;,该值匹配任何字符,不是;,并且至少出现一次,后跟一个;。如果您不在乎职位,请放[^;]*;。这几乎与第一个相似,但是;之前的字符也可能没有。将整个内容以^开头固定。

因此,对于您的第一,第三和第四位置示例,您将获得:

^[^;]+;[^;]*;[^;]+;[^;]+;

在如下查询中:

SELECT *
       FROM elbat
       WHERE regexp_like(nmuloc, '^[^;]+;[^;]*;[^;]+;[^;]+;');

db<>fiddle

通过将子表达式放在一组中,也就是在它们之间加上括号,并使用量化器(该组后面的花括号中的数字)可能会进一步改进。例如,([^;]+;){2}将匹配两个不为空的位置。您的示例将简化为:

^[^;]+;[^;]*;([^;]+;){2}

答案 1 :(得分:0)

虽然@stiky bit的答案是完全正确的,但还有另一种类似但也许更具可读性的解决方案:

SELECT *
   FROM elbat
   WHERE regexp_substr(nmuloc, '(.*?)(;|$)', 1, 1, '', 1) is not null
   AND   regexp_substr(nmuloc, '(.*?)(;|$)', 1, 3, '', 1) is not null
   AND   regexp_substr(nmuloc, '(.*?)(;|$)', 1, 4, '', 1) is not null;

db<>fiddle

优点:

  • 清楚地说明职位编号不能为空
  • 在任何情况下都具有通用模式,因此无需更改正则表达式
  • 可以使用任何正则表达式作为分隔符,不仅可以使用单个字符
  • 实际上提取项目,因此您可以使用任何功能对其进行进一步测试

缺点:

  • 比较冗长
  • 慢n倍,其中n是条件计数
  • 在每个非定界符号上回溯的原因更慢(最多2倍)

但是,根据我的经验,如果不对数十亿行进行查询,则效率差异很小。即使这样,磁盘读取仍会消耗大部分时间。

如何制作:

  • (。*?)(; | $)-延迟搜索以定界符或字符串结尾结尾的任何字符序列(可能为零长度)
  • 1-开始搜索的位置。默认为1。仅需转到下一个参数
  • 1、3或4-事件或模式
  • ''-match_parameter。可以用于设置匹配模式,但在这里也只能用于获取最后一个参数
  • 1-子表达式编号使regexp_substr仅返回第一个捕获组。即(。*?),即项目本身没有定界符。