【问题标题】:Search for Specific Character in a String - Oracle在字符串中搜索特定字符 - Oracle
【发布时间】:2014-01-24 15:32:05
【问题描述】:

我遇到过以下场景,

我有一个列名 Broker_name,其中的几个值是 'tpt Broker','Mark iii', 'Davidtpt'

这些值的最终输出应该是 'TPT Broker', 'Mark III','Davidtpt' 基本上我需要在一个字符串中加上几个预定义的值说 TPT 带有尾随空格、罗马字母等。

所以我创建了一个查找表

Column1 Column2
tpt      TPT
iii      III

能否请您告诉我如何检查字符串中是否存在特定字符,然后在表格上进行查找。基于此?

【问题讨论】:

  • 一个有趣的挑战。我将交叉连接到查找表并使用自定义聚合函数来执行替换。有点麻烦,说不定有人想出更聪明的办法……
  • 严格来说,你已经打破了 1NF。我会研究标准化数据库的方法(将字段拆分为原子成分)。之后,您的任务应该可以通过相当简单的 JOIN 来完成。

标签: sql oracle


【解决方案1】:

最后我在一个 MODEL 子句的帮助下设法处理了它:

SELECT id, broker_name FROM (
  SELECT id, broker_name, rn, val, replacement
  FROM brokers CROSS JOIN (SELECT rownum AS rn, val, replacement FROM lookup)
  MODEL
    PARTITION BY (id)
    DIMENSION BY (rn)
    MEASURES (val, replacement, broker_name)
    RULES (
      broker_name[ANY] = regexp_replace(nvl(broker_name[CV()-1], broker_name[CV()]),
                                        '(^|.*\W)' || val[CV()] || '(\W.*|$)',
                                        '\1' || replacement[CV()] || '\2')
    )
)
WHERE rn = (SELECT count(*) FROM lookup)
ORDER BY id, broker_name;

基本上它做了以下事情:

  1. 为查找表中的项目编号
  2. 在代理和查找之间执行交叉连接
  3. 对于每个代理,它会遍历查找值并进行替换(有点奇怪的正则表达式确保只替换整个单词)
  4. 最后只取每个代理的最后一行,其中包含所有替换完成后的值

这是SQL Fiddle

【讨论】:

  • 您好 Kombajin,当我尝试使用此数据“Mark iii/tpt”时,它没有用。在这里小提琴:- sqlfiddle.com/#!4/586f0/1
  • 这是因为 '/' 而我只使用了空格 (\s) 作为单词分隔符。但我们也可以使用任何非字母数字 (\W),查看更新后的查询(也在这里摆弄:sqlfiddle.com/#!4/586f0/4
  • 进程似乎运行了很长时间。我们还需要在另一列上进行类似的查找,我们可以将其组合到附加查询中吗?考虑源表中的 30,000 条记录和查找表中的 8 行。我认为这个过程可能会占用时间。还有其他选择吗?比如使用光标。
  • 要转换另一列,只需将其指定为另一个度量并将第二条规则添加到模型子句。关于性能:在我的笔记本电脑上,30k 记录在 20 秒内得到处理。如果您只有 8 条固定替换规则,您总是可以对它们进行硬编码而不是使用查找表,这样会快得多——当然是以灵活性为代价的。
【解决方案2】:

这有点困难,因为它需要不区分大小写(例如iIitpT)。通常,您只需使用 UPPER() 但您想保留其余列的大小写。它还必须替换许多不同的“不正确”字母。最后,您不希望看到像xxxtpt 这样的东西,如果您不能按空格(因为它可能在开头或结尾),这很困难。我不确定你的最终目标是什么。我能想到两个:

  1. 您只需要一个查询来正确格式化结果。
  2. 您希望更新任何包含此内容的值。

无论哪种方式,这是我能想到的最简单的解决方案(尽管是补救措施)。您必须考虑所有不正确的大小写组合(例如:tpt, tpT, tPt, tPT, Tpt, TPt)。这有点蛮力,但由于缺乏时间和简单性(尤其是在少数情况下),这会起作用:

代码:

Select
Trim(
  Replace(
  Replace(
  Replace(
  Replace(
  Replace(
  --note the ' ' are added so we can search for a isolated instance of
  --' tpt ' at the beginning or end of lines, as well as inbetween
  -- these space get Trim()'d at in the end of processing everything.
  Replace(' ' || test_t.text || ' ', ' tpt ', ' TPT ')
                                   , ' tpT ', ' TPT ')
                                   , ' tPt ', ' TPT ')
                                   --etc...
                                   , ' iii ', ' III ')
                                   --etc...
                                   , ' Iii ', ' III ')
                                   , ' IIi ', ' III ')
)
as text

--poor man's way of creating test tables
From
(
    Select 'tpt Broker' as text From dual UNION ALL
    Select 'Mark iii'   as text From dual UNION ALL
    Select 'Davidtpt'   as text From dual 
) test_t

结果:

TPT Broker
Mark III
Davidtpt

这几乎是穷人的方式。如果这是一个长期问题,我确信有更好的解决方案,但如果您的数据清理很容易,这可能会奏效。您也可以尝试使用REGEXP_REPLACE,或INSTR 的组合。如果我想到更好的东西,我会更新这篇文章。

编辑:

喂,我知道一定有更好的方法。使用 REGEXP_REPLACE 简化了很多。我无法使“不区分大小写”的查询起作用(可能忽略了某些东西)。相同的方法,但这里是一个真正体面的解决方案:

Select
    Trim(
        REGEXP_REPLACE(
        REGEXP_REPLACE(
          ' ' || text || ' '  ,  ' [tT][pP][tT] ', ' TPT ')
                              ,  ' [iI][iI][iI] ', ' III ')
        ) as text
From
(
    Select 'tPt Broker' as text From dual UNION ALL
    Select 'Mark iii'   as text From dual UNION ALL
    Select 'Davidtpt'   as text From dual 
) test_t

【讨论】:

    猜你喜欢
    • 2013-07-13
    • 2017-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-18
    • 1970-01-01
    • 2014-05-27
    相关资源
    最近更新 更多