【问题标题】:Replace string parts that appear twice Oracle替换出现两次的字符串部分 Oracle
【发布时间】:2021-02-09 16:29:41
【问题描述】:

我正在尝试在 Oracle 中研究如何隔离/突出显示连接字符串中的单词组合,如下所示:

Some words##Again words##More of this||@@@@||Some words##Again words##Other

我们的想法是找到出现恰好出现两次的单词组合并将它们替换为 0,这样我就剩下只出现一次的单词组合,或者在 ||@@@@|| 的左侧或在右侧。查询的结果应该是这样的:

突出显示

Some words##Again words##More of this||@@@@||Some words##Again words##**Other**

替换

0##0##More of this||@@@@||0##0##Other

为您提供有关串联的更多信息:左侧(||@@@@|| 之前)是我当前的客户记录,而右侧是以前的版本。通过进行替换,我可以揭示客户记录之间的任何差异。

我已尝试通过以下方式完成此操作:

  • regexp_replace:这并不完全适用于REGEXP_REPLACE(MY STRING,'((Some words){1,2})|((Again words){1,2})','0',1,0),因为出于某种原因,我的第一条记录中的字符串部分永远不会被正确替换。由于我需要匹配的单词组合的数量,我也达到了这个功能的限制;
  • nested CASE WHEN:显然也不起作用,因为 CASE WHEN - 甚至是嵌套的 - 在找到第一个匹配项时停止,但我需要检查并替换所有条件。
  • 我曾考虑过使用子选择,但由于此查询使用了我的架构中最大的表之一,因此除非基于每个客户,否则它将无法使用。它可能仍然无法正常工作...

更多信息,以便找到可靠、高效的解决方案:

  • 我有 34 个可能的单词组合要匹配
  • 我不知道哪些会在那里,除非我很明显地运行查询
  • 我不知道它们在连接字符串中的顺序

我希望这很清楚。有人有一些神奇的想法吗?

提前致谢

【问题讨论】:

    标签: string oracle regexp-replace


    【解决方案1】:

    您可以使用递归子查询因式分解子句在每次迭代中替换一个重复项:

    WITH replaced ( value, start_char ) AS (
      SELECT REGEXP_REPLACE(
               value,
               '(##|^)([^#]+?)((##[^#]+?)*\|\|@@@@\|\|([^#]+?##)*)\2(##|$)',
               '\10\30\6',
               1
             ),
             REGEXP_INSTR(
               value,
               '(##|^)([^#]+?)((##[^#]+?)*\|\|@@@@\|\|([^#]+?##)*)\2(##|$)',
               1
             )
      FROM   table_name
    UNION ALL
      SELECT REGEXP_REPLACE(
               value,
               '(##|^)([^#]+?)((##[^#]+?)*\|\|@@@@\|\|([^#]+?##)*)\2(##|$)',
               '\10\30\6',
               start_char + 1
             ),
             REGEXP_INSTR(
               value,
               '(##|^)([^#]+?)((##[^#]+?)*\|\|@@@@\|\|([^#]+?##)*)\2(##|$)',
               start_char + 1
             )
      FROM   replaced
      WHERE  start_char > 0
    )
    SELECT value
    FROM   replaced
    WHERE  start_char = 0;
    

    其中,对于样本数据:

    CREATE TABLE table_name ( value ) AS
    SELECT 'Some words##Again words##More of this||@@@@||Some words##Again words##Other' FROM DUAL UNION ALL
    SELECT '333##123##789##555||@@@@||123##456##789##222##333' FROM DUAL;
    

    输出:

    |价值 | | :------------------------------------------------ | | 0##0##更多||@@@@||0##0##其他 | | 0##0##0##555||@@@@||0##456##0##222##0 |

    db小提琴here

    说明:

    正则表达式匹配:

    • (##|^) 两个# 字符或字符串的开头^(在第一个捕获组() 中);
    • ([^#]+?)一个或多个不是#的字符(在第二个捕获组()中);
    • (第三个捕获组的开始;
      • (##[^#]+?)* 两个# 字符后跟一个或多个非# 字符(在第四个捕获组() 中)都重复了零个或多个* 次;
      • \|\|@@@@\|\| 然后是两个 | 字符、四个 @ 字符和两个 | 字符;
      • ([^#]+?##)* 然后是多个非# 字符,后跟两个# 字符(在第5 个捕获组() 中);
    • )第三个捕获组结束;
    • \2 第二个捕获组的副本;那么
    • (##|$) 两个 # 字符或字符串结尾 $(在第 6 个捕获组中)。

    替换为:

    • \10\30\6 这是第一个捕获组的内容,然后是零(替换第二个捕获组),然后是第三个捕获组的内容,然后是第二个零(替换匹配的副本),然后是第六个捕获组的内容。

    查询将替换字符串中的一对重复项(如果它们存在),REGEXP_INSTR 将找到匹配的开始并将值放入valuestart_char(分别);然后在下一次迭代中,正则表达式将从上一个匹配开始的下一个字符开始查找,以便它将逐渐在字符串中移动查找匹配项,直到找不到更多重复项并且REGEXP_REPLACE 将不会执行替换和REGEXP_INSTR 将返回0 并且迭代将终止。

    最终查询过滤器返回唯一的最终迭代级别(当所有重复项都已被替换时)。

    【讨论】:

    • 谢谢!当我把它放在我的源查询中时它会起作用。你能简要解释一下正则表达式和替换实际上在做什么吗?我对正则表达式完全陌生,很想了解更多信息。另外,“prev_value”到底有什么用途?
    • @Postino 更新了解释。 prev_value 是我在早期版本的查询中使用的东西,当时我试图在没有 REGEXP_INSTR 的情况下执行此操作,但该方法不起作用,现在它与查询无关,但我忘记删除它;现在已删除。
    • 太棒了!需要了解更多关于正则表达式的详细信息。在某些情况下,它们非常方便。
    • 这是否适用于任何包含相同分隔符但长度可变并且可能包含其他字符(如逗号)的字符串?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-14
    • 2021-11-09
    • 2017-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-27
    相关资源
    最近更新 更多