POSIX ERE 查找重复子串的正则表达式

POSIX ERE Regular expression to find repeated substring

我有一组字符串,其中至少包含 1 个值,最多包含 3 个值,格式如下:

123;456;789
123;123;456
123;123;123
123;456;456
123;456;123

我正在尝试编写一个正则表达式,以便我可以找到在同一个字符串上重复的值,所以如果你有 123;456;789 它会 return null 但如果你有123;456;456 它将 return 456 并且对于 123;456;123 return 123

我设法写出了这个表达式:

(.*?);?([0-9]+);?(.*?)

它的工作原理是它 returns null 当没有重复值但它不 return 正是我需要的值时,例如:对于字符串123;456;456 it returns 123;456;456 对于字符串 123;123;123 it returns 123;123

我需要的只是 return 表达式的 ([0-9]+) 部分的值,根据我的阅读,这通常使用非捕获组来完成。但是要么我做错了,要么 Oracle SQL 不支持这个,就好像我尝试使用 ?: 语法结果不是我所期望的。

关于如何在 oracle sql 上进行此操作的任何建议?此表达式的目的是在查询中使用它。

SELECT REGEXP_SUBSTR(column, "expression") FROM DUAL;

编辑:

其实根据https://docs.oracle.com/cd/B12037_01/appdev.101/b10795/adfns_re.htm

Oracle Database implements regular expression support compliant with the POSIX Extended Regular Expression (ERE) specification.

根据https://www.regular-expressions.info/refcapture.html

Non-capturing group is not supported by POSIX ERE

如果你只有三个子串,那么你可以使用暴力破解的方法。它不是特别漂亮,但它应该能胜任:

select (case when val1 in (val2, val3) then val1
             when val2 = val3 then val2
        end) as repeated
from (select t.*,
             regexp_substr(col, '[^;]+', 1, 1) as val1,
             regexp_substr(col, '[^;]+', 1, 2) as val2,
             regexp_substr(col, '[^;]+', 1, 3) as val3
      from t
     ) t
where val1 in (val2, val3) or val2 = val3;

This answer 描述了如何 select 来自正则表达式的匹配组。所以使用它,

SELECT regexp_substr(column, '(\d{3}).*', 1, 1, NULL, 1) from dual;
#                                                       ^ Select group 1

Working demo 正则表达式(由 OP 提供)。

请耐心等待并考虑这种不同的方法。以不同的方式看待问题,并以一种让您更灵活地查看数据的方式将其分解。它可能适用于您的情况,也可能不适用于您的情况,但希望记住总是有不同的方法来解决问题。

如果您将字符串变成行以便您可以对它们执行标准 SQL 会怎么样?这样,您不仅可以对重复的元素进行计数,还可以应用聚合函数来寻找跨集合或其他东西的模式。

那就考虑一下吧。第一个 Common Table Expression (CTE) 构建原始数据集。第二个 tbl_split 将该数据转换为列表中每个元素的一行。取消注释紧随其后的 select 以查看。来自拆分数据的最后一个查询 selects,显示元素在 id 的数据中出现的频率。取消对 HAVING 行的注释,以将输出限制为那些在您之后的数据中出现不止一次的元素。

通过行中的数据,您可以了解如何将其他聚合函数应用于切片和切块以揭示模式等。

SQL> with tbl_orig(id, str) as (
     select 1, '123;456;789' from dual union all
     select 2, '123;123;456' from dual union all
     select 3, '123;123;123' from dual union all
     select 4, '123;456;456' from dual union all
     select 5, '123;456;123' from dual
   ),
   tbl_split(id, element) as (
   select id,
          regexp_substr(str, '(.*?)(;|$)', 1, level, NULL, 1) element
   from tbl_orig
   connect by level <= regexp_count(str, ';')+1
   and prior id = id
   and prior sys_guid() is not null
   )
   --select * from tbl_split;
   select distinct id, element, count(element)
   from tbl_split
   group by id, element
   --having count(element) > 1
   order by id;

        ID ELEMENT     COUNT(ELEMENT)
---------- ----------- --------------
         1 123                      1
         1 456                      1
         1 789                      1
         2 123                      2
         2 456                      1
         3 123                      3
         4 123                      1
         4 456                      2
         5 123                      2
         5 456                      1

10 rows selected.

SQL>