【问题标题】:POSIX ERE Regular expression to find repeated substringPOSIX ERE 正则表达式查找重复的子字符串
【发布时间】:2017-10-04 23:26:30
【问题描述】:

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

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

我正在尝试编写一个正则表达式,以便可以找到在同一字符串上重复的值,因此如果您有 123;456;789,它将返回 null,但如果您有 123;456;456,它将返回 456 和对于123;456;123 返回123

我设法写了这个表达式:

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

它的工作原理是在没有重复值时返回null,但它并不完全返回我需要的值,例如:对于字符串123;456;456,它返回123;456;456,对于字符串@ 987654335@它返回123;123

我需要的是只返回表达式的([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 数据库实现了符合 POSIX 扩展正则表达式 (ERE) 规范的正则表达式支持。

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

POSIX ERE 不支持非捕获组

【问题讨论】:

  • 在我看来,你不需要正则表达式。您需要修复您的数据模型,这样您就不会将数字列表存储在分隔字符串中。
  • 我知道存储这样的值不是最优的,但它不是我的数据库,所以我不能只是“修复数据模型”
  • 您没有告诉我们示例中最后一行的内容:123123;456;123 中的重复“字符串”吗?另外,如果字符串看起来像 123;123;456;456 怎么办?您需要返回什么 - 在字符串中找到的第一个子字符串(实际上是令牌)?
  • 另外:双引号是字符串的一部分,还是您只是将它们作为字符串分隔符?在 Oracle 中,字符串定界符是单引号而不是双引号。
  • 所以我认为这将是一个有趣的做法(使用纯正则表达式)来做我相信你想做的事情。请注意,这使用 PCRE 正则表达式,并且可能在 POSIX 中不起作用(很可能,我的意思是它不起作用),但是,它确实回答了您的问题"(?:(\d+);(?(?=\1)(?<duplicate>\d+)|(\d+));(?(?=\1|\2|\3)(?<duplicate>\d+)|(\d+)))"。在此处查看它的使用情况:regex101.com/r/31WULs/1

标签: regex posix oracle-sqldeveloper regexp-substr posix-ere


【解决方案1】:

This answer 描述了如何从正则表达式中选择匹配组。所以使用它,

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

Working demo 的正则表达式(礼貌:OP)。

【讨论】:

  • 小心,这匹配 '123;456;123456' 中的 123
  • 我不知道 regexp_substr 可以接受更多参数,这正是我想要完成的,我不认为我能找到更好的解决方案。泰
  • @Gary_W 在这种情况下这不是问题,因为由; 分隔的所有值都具有相同的长度,但是如果您可以有不同长度的值,则此表达式将解决该问题:((^|;)(\d+)).*\1(;|$)
  • @ArtemioRamirez,实际上你的正则表达式仍然匹配123;456;4123,你需要的是\b\1 之前,我不确定它是否支持。
  • @Gary_W 让我再次到达那里,我不确定 \b 究竟是如何工作的,但它可能可以以不同的方式修复。我试试
【解决方案2】:

如果您只有三个子字符串,那么您可以使用蛮力方法。它不是特别漂亮,但它应该可以完成这项工作:

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;

【讨论】:

  • 现在我很好奇是否可以只用正则表达式来解决这个问题,但是由于时间限制,它必须被暴力破解
  • 如果有 NULL 元素,恐怕你的正则表达式会失败。请改用此表格:regexp_substr(col, '(.*?)(;|$)', 1, 1, NULL, 1)。证明见这里:stackoverflow.com/questions/31464275/…
  • @Gary_W 。 . .它似乎对我有用空值:rextester.com/EKIXZ93706.
  • 我的立场是正确的(无论如何,对于这个例子)!我将研究您的示例,但仍建议出于提供的链接中的原因更改正则表达式。它仍然有效:-)
【解决方案3】:

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

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

然后考虑一下。第一个公用表表达式 (CTE) 构建原始数据集。第二个,tbl_split,将数据转换为列表中每个元素的一行。取消注释紧随其后的选择以查看。最后一个查询从拆分数据中进行选择,显示元素在 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>

【讨论】:

  • 你是对的,评估不同的方法来处理可能会让你看到你可能遗漏的东西总是很好的。对于我的特殊情况,尽管我已经标记的答案仍然是最合适的。因为除了识别具有重复值的行以及哪个值是重复的行之外,我不需要也不想进一步操作数据
  • 在底部查看我的编辑。我是说这种方法是另一种可以回答您的问题的方法,同时也可以设置一个以不同方式查看数据的框架。只是以不同的方式解决问题。
猜你喜欢
  • 2022-12-05
  • 2021-12-26
  • 2016-05-16
  • 2012-05-12
  • 1970-01-01
  • 2020-06-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多