【问题标题】:String removal in Oracle PL/SQLOracle PL/SQL 中的字符串删除
【发布时间】:2014-10-19 16:56:42
【问题描述】:

我需要从输入字符串中删除某些关键字并返回新字符串。关键字存储在另一个表中,如 MR、MRS、DR、PVT、PRIVATE、CO、COMPANY、LTD、LIMITED 等。它们是两种关键字 LEADING - MR、MRS、DR 和 TRAILING - PVT、PRIVATE、CO、COMPANY, LTD、LIMITED 等。因此,如果关键字是 LEADING,那么我们必须从开头删除它,如果它是 TRAILING,那么我们必须从最后删除它。例如- MR Jones MRS COMPANY 应该返回 JONES MRSMR MRS Jones PVT COMPANY 应该返回 JONES (如在第一次迭代中 MRPVT 将被修剪,然后单词将变为 MRS JONES PVT 并在第二次迭代中将变为 @ 987654328@。同样MR MRS Doe PVT COMPANY LTD最终会返回DOE

我必须通过 PL/SQL 来完成。我编写了以下代码,但如果开头或结尾有多个关键字,它将删除所有关键字。当我遍历关键字光标时的原因,如果关键字不在末尾并且循环已经迭代,那么我们不能重用该关键字来替换。请注意,结尾或开头都可以有 n 个关键字:

CREATE OR REPLACE FUNCTION replace_keyword (p_in_name IN VARCHAR2)
   RETURN VARCHAR2
IS
   l_name   VARCHAR2 (4000);

   CURSOR c
   IS
      SELECT *
        FROM RSRV_KEY_LKUPS
       WHERE ACTIVE = 'Y';
BEGIN
    l_name := TRIM (p_in_name); 

   --Now inside the function we’ll loop through this cursor something like below and replace the value in the input name:

   FOR rec IN c
   LOOP
      IF     UPPER (rec.POSITION) = 'LEADING'
         AND INSTR (UPPER (l_name), UPPER (rec.KEY_WORD || ' '), 1) > 0
      THEN                                        --Rule 3:remove leading name
         DBMS_OUTPUT.PUT_LINE ('Value >>' || rec.KEY_WORD);
         l_name := LTRIM (UPPER (l_name), rec.KEY_WORD || ' ');

      ELSIF     UPPER (rec.POSITION) = 'TRAILING'
            AND INSTR (UPPER (l_name), UPPER (' ' || rec.KEY_WORD), -1) > 0
      THEN                                       --Rule 4:remove trailing name
         DBMS_OUTPUT.PUT_LINE ('Value >>' || rec.KEY_WORD);
         l_name := RTRIM (UPPER (l_name), ' ' || rec.KEY_WORD);      
      END IF;

      l_name := l_name;
   END LOOP;

   l_name := REGEXP_REPLACE (l_name, '[[:space:]]{2,}', ' '); --Remove multiple spaces in a word and replace with single blank space
   l_name := TRIM (l_name); --Remove the leading and trailing blank spaces
   RETURN l_name;
EXCEPTION
   WHEN OTHERS
   THEN
      raise_application_error (
         -20001,
         'An error was encountered - ' || SQLCODE || ' -ERROR- ' || SQLERRM);
END;
/

非常感谢您的任何帮助。

编辑 样本输入 1

MR MRS Jones PVT COMPANY 

输出

JONES

示例输入 2

MR MRS Doe PVT COMPANY LTD 

输出

DOE

【问题讨论】:

  • 如果您显示示例数据(通过编辑您的问题)以及您实际想要从中返回的内容,将会很有帮助。
  • @Gordon - 最后添加了示例输入输出,尽管它已经存在于问题中。
  • @yamny 在我的情况下如何使用正则表达式?
  • 问题不在于替换,而是如何在我已经添加的循环之上使用另一个循环,它将检查是否所有关键字都被删除。
  • @Sid 好的,现在我完全理解了你的问题,我认为可以使用regexp_replace,但我不知道它是否有效。

标签: sql oracle replace plsql


【解决方案1】:

如果要确保在开头找到前导关键字,则应仅在 INSTR 返回 1 时将其删除:

替换

IF UPPER (rec.POSITION) = 'LEADING'
   AND INSTR (UPPER (l_name), UPPER (rec.KEY_WORD || ' '), 1) > 0

IF UPPER (rec.POSITION) = 'LEADING'
   AND INSTR (UPPER (l_name), UPPER (rec.KEY_WORD || ' '), 1) = 1

替换

  ELSIF     UPPER (rec.POSITION) = 'TRAILING'
        AND INSTR (UPPER (l_name), UPPER (' ' || rec.KEY_WORD), -1) > 0

通过

  ELSIF UPPER (rec.POSITION) = 'TRAILING'
        AND INSTR (UPPER (l_name), UPPER (' ' || rec.KEY_WORD), -1) = (LENGTH(l_name)-LENGTH(rec.key_word) +1)

对于多个关键字的问题,你必须循环你的 for 循环:

keyword_found BOOLEAN;
LOOP
  keyword_found = false;
  FOR rec IN c
       -- when you find a keyword
       keyword_found := true;
  END LOOP;
  EXIT WHEN NOT(keyword_found);
END LOOP;

【讨论】:

  • 您是否将keyword_found = true 放入“IF THEN”部分?还编辑了我的代码。
  • 是的。我添加了它们......但请忽略它。
  • 忘记了冒号:keyword := true;
【解决方案2】:

我认为可以通过单个查询来完成(如果您出于某种原因坚持,可以将其包装在 plsql 函数中):

Here is a sqlfiddle demo

with inpt as (select 'MR Jones MRS COMPANY' text from dual)
select listagg(t1.word, ' ') within group (order by ord) new_text 
from (
select w.*, words.*, 
sum(case when nvl(POSITION, 'TRAILING') = 'TRAILING'  then 1 else 0 end) over(order by ord rows between unbounded preceding and current row) l,
sum(case when nvl(POSITION, 'LEADING') = 'LEADING' then 1 else 0 end) over(order by ord desc rows between unbounded preceding and current row) t
from 
(select regexp_substr(inpt.text, '[^ ]+',1,level) word , level ord 
from inpt 
connect by level <= regexp_count(inpt.text, ' ') + 1) words left outer join RSRV_KEY_LKUPS w on w.KEY_WORD = words.word
 ) t1
where t1.t > 0 and t1.l > 0

编辑:解释:

'with' 子句只是将您的输入字符串作为一列(不是必需的)。

别名为“words”的内部选择是用于将单词拆分为行的已知技术(请注意,我使用ord 列保持顺序)。

现在我们可以将输入字符串的单词与表“RSRV_KEY_LKUPS”中的关键字进行左外连接,这将为输入中的每个单词提供前导或尾随或 null(如果它不存在) )

所以,到目前为止我们有(输入"MR Jones MRS COMPANY"):

KEY_WORD    POSITION    WORD    ORD 
----------------------------------- 
MR          LEADING     MR      1  
(null)      (null)      Jones   2  
MRS         LEADING     MRS     3  
COMPANY     TRAILING    COMPANY 4 

现在是棘手的部分(也许有更好的方法) - 我们需要以某种方式知道要删除哪个单词,它应该是所有 LEADING 直到“更改”,这意味着直到我们找到一个 null 或一个 TRAILING(顶部向下)和所有 TRAILING 直到“变化”,这意味着 null 或 LEADING(自下而上)。所以我使用了一个已知的技术来进行累计,只要我们仍然归零,我们就需要删除该行(一旦我们得到“改变”,我们就会有一些值)。

就是这样,我们现在需要做的就是将行重新收集到一个新字符串中,从 11gr2 开始,我们可以完全使用 LISTAGG

【讨论】:

    猜你喜欢
    • 2011-07-27
    • 1970-01-01
    • 2018-03-23
    • 1970-01-01
    • 2019-02-06
    • 2013-03-24
    • 1970-01-01
    • 1970-01-01
    • 2017-11-13
    相关资源
    最近更新 更多