【问题标题】:How to find the exact match of a string and replace in Oracle?如何在Oracle中找到字符串的完全匹配并替换?
【发布时间】:2020-01-13 00:19:01
【问题描述】:

如果单词出现在“单词”列中,我正在尝试替换句子中的单词,并带有一个超链接以及它的单词和 id。该表包含列 id 单词和句子。下面的代码是我从这里的一位乐于助人的伙伴那里得到的。谢谢。

https://dbfiddle.uk/?rdbms=oracle_18&fiddle=8fe103264dc650ad4bd87b20f9c6931a

Create table temp(
  id       NUMBER,
  word     VARCHAR2(1000),
  Sentence VARCHAR2(2000)
);

insert into temp
SELECT 1,'automation testing', 'automtestingation testing is popular kind of testing' FROM DUAL UNION ALL
SELECT 2,'testing','manual testing' FROM DUAL UNION ALL
SELECT 3,'manual testing','this is an old method of testing' FROM DUAL UNION ALL
SELECT 5,'B-number analysis','B-number analysis table' FROM DUAL UNION ALL
SELECT 6,'B-number analysis table','testing B-number analysis' FROM DUAL;

MERGE INTO temp dst
USING (
  WITH ordered_words ( rn, id, word ) AS (
    SELECT ROW_NUMBER() OVER ( ORDER BY LENGTH( word ) ASC, word DESC ),
           id,
           word
    FROM   temp
  ),
  sentences ( rid, sentence, rn ) AS (
    SELECT ROWID,
           sentence,
           COUNT(*) OVER () + 1
    FROM temp
  UNION ALL
    SELECT s.rid,
           REGEXP_REPLACE(
             REGEXP_REPLACE(
               s.sentence,
               '(^|[^a-z])' || w.word || '($|[^a-z])',
               '\1' || 'http://localhost/'|| w.id ||'/<u>'||w.word ||'<u>' || '\2',
               1,
               0,
               'i'
              ),
             '(^|[^a-z])' || w.word || '($|[^a-z])',
             '\1' || w.word || '\2',
               1,
               0,
               'i'
              ),
           s.rn - 1
    FROM   sentences s
           INNER JOIN ordered_words w
           ON ( s.rn - 1 = w.rn ) 
  )
  SELECT rid, sentence
  FROM   sentences
  WHERE  rn = 1
) src
ON ( dst.ROWID = src.RID )
WHEN MATCHED THEN
  UPDATE
  SET    sentence = src.sentence;

要替换的值是https://localhost/"id"/"word" 如果看到id = 5的值(B-number分析)句子是https://localhost/6/localhost/5/B-number分析表但是实际值应该是https://localhost/6/B-数字分析表。

当前输出:

ID   WORD                SENTENCE
1   automation testing  automtestingation http://localhost/2/<u>testing<u> 
                        is popular kind of http://localhost/2/<u>testing<u>
2   testing             http://localhost/3/<u>manual 
                        http://localhost/2/<u>testing<u><u>
3   manual testing      this is an old method of 
                        http://localhost/2/<u>testing<u>
5   B-number analysis   http://localhost/6/<u>http://localhost/5/<u>B- 
                        number analysis<u> table<u>
6   B-number analysis table http://localhost/2/<u>testing<u> 
                        http://localhost/5/<u>B-number analysis<u>

【问题讨论】:

    标签: regex oracle replace pattern-matching regexp-replace


    【解决方案1】:

    分两步完成:

    1. 首先将字符串替换为某些包装器中不会出现在您的文本中的 id(即 testing 映射到 ${2}
    2. 然后,完成所有替换后,将包装好的 id 替换为 url(即 ${2} 映射到 http://localhost/2/&lt;u&gt;testing&lt;/u&gt;

    Oracle 设置

    Create table temp(
      id       NUMBER,
      word     VARCHAR2(1000),
      Sentence VARCHAR2(2000)
    );
    
    insert into temp
    SELECT 1,'automation testing', 'automtestingation testing is popular kind of testing' FROM DUAL UNION ALL
    SELECT 2,'testing','manual testing' FROM DUAL UNION ALL
    SELECT 3,'manual testing','this is an old method of testing' FROM DUAL UNION ALL
    SELECT 4,'punctuation','automation testing,manual testing,punctuation,automanual testing-testing' FROM DUAL UNION ALL
    SELECT 5,'B-number analysis','B-number analysis table' FROM DUAL UNION ALL
    SELECT 6,'B-number analysis table','testing B-number analysis' FROM DUAL UNION ALL
    SELECT 7,'Not Matched','testing testing testing' FROM DUAL;
    

    合并

    MERGE INTO temp dst
    USING (
      WITH ordered_words ( rn, id, word ) AS (
        SELECT ROW_NUMBER() OVER ( ORDER BY LENGTH( word ) ASC, word DESC ),
               id,
               word
        FROM   temp
      ),
      sentences_with_ids ( rid, sentence, rn ) AS (
        SELECT ROWID,
               sentence,
               ( SELECT COUNT(*) + 1 FROM ordered_words )
        FROM   temp
      UNION ALL
        SELECT s.rid,
               REGEXP_REPLACE(
                 REGEXP_REPLACE(
                   s.sentence,
                   '(^|\W)' || w.word || '($|\W)',
                   '\1${'|| w.id ||'}\2'
                  ),
                 '(^|\W)' || w.word || '($|\W)',
                 '\1${' || w.id || '}\2'
               ),
               s.rn - 1
        FROM   sentences_with_ids s
               INNER JOIN ordered_words w
               ON ( s.rn - 1 = w.rn ) 
      ),
      sentences_with_words ( rid, sentence, rn ) AS (
        SELECT rid,
               sentence,
               ( SELECT COUNT(*) + 1 FROM ordered_words )
        FROM   sentences_with_ids
        WHERE  rn = 1
      UNION ALL
        SELECT s.rid,
               REPLACE(
                 s.sentence,
                 '${' || w.id || '}',
                 'http://localhost/' || w.id || '/<u>' || w.word || '</u>'
               ),
               s.rn - 1
        FROM   sentences_with_words s
               INNER JOIN ordered_words w
               ON ( s.rn - 1 = w.rn ) 
      )
      SELECT rid, sentence
      FROM   sentences_with_words
      WHERE  rn = 1
    ) src
    ON ( dst.ROWID = src.RID )
    WHEN MATCHED THEN
      UPDATE
      SET    sentence = src.sentence;
    

    输出

    身份证 |字 |句子 -: | :------------------------ | :------------------------------------------------ -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -- 1 |自动化测试|自动测试 http://localhost/2/testing 是一种流行的 http://localhost/2/testing 2 |测试 | http://localhost/3/手动测试 3 |手动测试 |这是 http://localhost/2/testing 的旧方法 4 |标点符号 | http://localhost/1/自动化测试,http://localhost/3/手动测试,http://localhost/4/标点符​​号 u>,automanual http://localhost/2/testing-http://localhost/2/testing 5 | B数分析| http://localhost/6/B数分析表 6 | B数分析表| http://localhost/2/测试 http://localhost/5/B数分析 7 |不匹配 | http://localhost/2/测试 http://localhost/2/测试 http://localhost/2/测试

    db小提琴here


    更新

    转义单词中的任何特殊正则表达式字符:

    MERGE INTO temp dst
    USING (
      WITH ordered_words ( rn, id, word, regex_safe_word ) AS (
        SELECT ROW_NUMBER() OVER ( ORDER BY LENGTH( word ) ASC, word DESC ),
               id,
               word,
               REGEXP_REPLACE( word, '([][)(}{|^$\.*+?])', '\\\1' )
        FROM   temp
      ),
      sentences_with_ids ( rid, sentence, rn ) AS (
        SELECT ROWID,
               sentence,
               ( SELECT COUNT(*) + 1 FROM ordered_words )
        FROM   temp
      UNION ALL
        SELECT s.rid,
               REGEXP_REPLACE(
                 REGEXP_REPLACE(
                   s.sentence,
                   '(^|\W)' || w.regex_safe_word || '($|\W)',
                   '\1${'|| w.id ||'}\2'
                  ),
                 '(^|\W)' || w.regex_safe_word || '($|\W)',
                 '\1${' || w.id || '}\2'
               ),
               s.rn - 1
        FROM   sentences_with_ids s
               INNER JOIN ordered_words w
               ON ( s.rn - 1 = w.rn ) 
      ),
      sentences_with_words ( rid, sentence, rn ) AS (
        SELECT rid,
               sentence,
               ( SELECT COUNT(*) + 1 FROM ordered_words )
        FROM   sentences_with_ids
        WHERE  rn = 1
      UNION ALL
        SELECT s.rid,
               REPLACE(
                 s.sentence,
                 '${' || w.id || '}',
                 'http://localhost/' || w.id || '/<u>' || w.word || '</u>'
               ),
               s.rn - 1
        FROM   sentences_with_words s
               INNER JOIN ordered_words w
               ON ( s.rn - 1 = w.rn ) 
      )
      SELECT rid, sentence
      FROM   sentences_with_words
      WHERE  rn = 1
    ) src
    ON ( dst.ROWID = src.RID )
    WHEN MATCHED THEN
      UPDATE
      SET    sentence = src.sentence;
    

    输出

    身份证 |字 |句子 -: | :------------------------ | :------------------------------------------------ -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -- 1 |自动化测试|自动测试 http://localhost/2/testing 是一种流行的 http://localhost/2/testing 2 |测试 | http://localhost/3/手动测试 3 |手动测试 |这是 http://localhost/2/testing 的旧方法 4 |标点符号 | http://localhost/1/自动化测试,http://localhost/3/手动测试,http://localhost/4/标点符​​号 u>,automanual http://localhost/2/testing-http://localhost/2/testing 5 | B数分析| http://localhost/6/B数分析表 6 | B数分析表| http://localhost/2/测试 http://localhost/5/B数分析 7 |不匹配 | http://localhost/2/测试 http://localhost/2/测试 http://localhost/2/测试 8 | ^[($ | http://localhost/2/testing 字符 http://localhost/8/^[($ 需要在正则表达式中转义

    db小提琴here

    【讨论】:

    • 谢谢你,MT0。当我针对实际数据运行脚本时,它在正则表达式中给出了 ORA-12728 无效范围。有没有办法打印/识别导致问题的数据/文本?
    • @Ana 更新以转义单词中的特殊字符。
    • 我使用SELECT special_char, COUNT (*) FROM ( SELECT DISTINCT id, SUBSTR(word,REGEXP_INSTR (word, '[^a-z|A-Z|0-9| ]'),1) special_char FROM temp WHERE REGEXP_LIKE (temp, '[^a-z|0-9| ]', 'i') ) temp group by special_char; 查询了单词列中使用的特殊字符,我发现':*; + ™ – � \ _ / = % [ ? > - ( . $ , ) ! " " & @ # " ^。我会将这些包含在代码中。
    • @Ana 你不需要全部逃避。只有正则表达式的特殊字符,它们应该已经列在我的代码中。
    • 嗨 MT0,我正在对一个包含 45k 单词和句子的表运行此代码。它运行了超过 15 个小时并且仍在运行。你认为我们必须修改代码来提高性能吗?或者有没有办法确定在任何给定时间更新了多少行?
    猜你喜欢
    • 2019-05-17
    • 1970-01-01
    • 1970-01-01
    • 2013-05-21
    • 2020-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-01
    相关资源
    最近更新 更多