替换出现两次Oracle的字符串部分

问题描述

我正在尝试在Oracle中解决如何隔离/突出显示如下所示的串联字符串中的单词组合:

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

这个想法是找到出现恰好两次的单词组合,然后将其替换为0,这样我就剩下了只出现一次的单词组合,位于{{1的左侧}}或在右侧。查询结果应该是这样的:

突出显示

||@@@@||

已替换

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

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

我尝试通过使用以下方法完成此任务:

  • regexp_replace :由于某些原因,我的第一条记录中的字符串部分从未正确替换过,因此不能完全与||@@@@||一起使用。由于我需要匹配的单词组合数量,我也达到了此功能的极限;
  • 嵌套的CASE WHEN :显然不起作用,因为CASE WHEN甚至嵌套-在找到第一个匹配项时停止,但是我需要检查并替换所有条件。
  • 我已经考虑过使用子选择,但是由于此查询使用了我的架构中最大的表之一,因此除非按每个客户使用,否则此表将无法使用。而且可能仍然无法正常工作...

更多信息以找到可靠的高性能解决方案:

  • 我有34种可能的单词组合要匹配
  • 除了我显然运行查询之外,我不知道哪个会在那里
  • 我不知道它们在连接字符串中的排列顺序

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

预先感谢

解决方法

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

WITH replaced ( value,start_char ) AS (
  SELECT REGEXP_REPLACE(
           value,'(##|^)([^#]+?)((##[^#]+?)*\|\|@@@@\|\|([^#]+?##)*)\2(##|$)','\10\30\6',1
         ),REGEXP_INSTR(
           value,1
         )
  FROM   table_name
UNION ALL
  SELECT REGEXP_REPLACE(
           value,start_char + 1
         ),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;

输出:

| VALUE                                 |
| :------------------------------------ |
| 0##0##More of this||@@@@||0##0##Other |
| 0##0##0##555||@@@@||0##456##0##222##0 |

db 提琴here

说明:

正则表达式匹配:

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

将其替换为:

  • $,它是第一个捕获组的内容,然后是一个零(替换第二个捕获组),然后是第三个捕获组的内容,然后是第二个零(替换匹配的重复项),然后是第六捕获小组。

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

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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...