【问题标题】:How do I extract data between two strings based on a pattern in Oracle SQL如何根据 Oracle SQL 中的模式提取两个字符串之间的数据
【发布时间】:2021-02-15 04:39:07
【问题描述】:

我想根据特定模式从 oracle SQL 中 CLOB 类型的列中提取数据。我用正则表达式尝试了不同的东西,到目前为止没有任何效果。

PFB 有关数据外观和预期输出的示例。 样本数据:

我应该提取单词 LIST 之前的 CLOB 列,直到 .(dot) 之前的一个单词 PS:CLOB 可以在模式内有 CR LF / 回车。 预期输出:

【问题讨论】:

  • 为什么12346在输出中不包含[SOC]和[RAW]?
  • 您要提取的内容不符合col_b 的值。如果要在点之前提取,那么[Location=BLAH][SOC] 也应该在结果集中。
  • 输入字符串包括换行符(正如您自己所说)。这些换行符在您的输出中消失了。这是为什么?这是要求的一部分吗?你没有提到它;如果这是要求的一部分,您应该这样说。
  • 另外,请将示例数据以纯文本(而非图像)的形式发布 - 我们不能使用您发布的内容来测试我们的解决方案。

标签: sql oracle


【解决方案1】:

我会这样做。注意几点:

  1. 输出保留输入中存在的换行符。你没有 说出关于删除它们的任何信息;但是,您的输出未显示 他们。无论如何 - 如果需要,它们可以被移除,但这是一个 不相关的过程。
  2. 您说的是“单词”,但显然您使用的是不同的意思 来自正则表达式中的常见用法。在正则表达式中,“字 characters" 只是字母、数字和下划线;但您的 “单词”包括括号、等号,还有谁知道呢。我将“单词”解释为任何 连续的非空白字符序列。

我们可以通过以下方式重新创建您的数据。当您在这里提出问题时,您应该以这种方式提供示例数据 - 而不是我们无法在 SQL 编辑器中复制和粘贴的图像。

CREATE TABLE sample_data( col_a varchar2(20), col_b CLOB );

INSERT INTO sample_data VALUES
('12345', to_clob(
'Created:2/28/2019
Updated:1/19/2021
LIST:[ABC][DEF][GHI]
[LMNO][PQRST]
[Location=BLAH].[City=BLAH]'));

INSERT INTO sample_data VALUES
('12346', to_clob(
'Created:2/28/2019
Updated:1/19/2021
LIST:[ABC][DEF][GHI]
[LMNO][PQRST]
[SOC].[RAW]'));

commit;

然后这里是查询和输出。请注意,根据您的界面(在我的例子中:SQL Developer,它使用类似 SQL*Plus 的界面),您可能需要更改一些设置以使输出不会被截断。特别是在 SQL*Plus 中,CLOB 列默认被截断为 80 个字符;我不得不

set long 100

所以 - 查询和输出:

select col_a, col_b,
       regexp_substr(col_b, '(\s|^)(LIST:[^.]*?)\s+\S+\.', 1, 1, null, 2)
         as result
from   sample_data
;

COL_A COL_B                          RESULT                        
----- ------------------------------ ------------------------------
12345 Created:2/28/2019              LIST:[ABC][DEF][GHI]          
      Updated:1/19/2021              [LMNO][PQRST]                 
      LIST:[ABC][DEF][GHI]                                         
      [LMNO][PQRST]                                                
      [Location=BLAH].[City=BLAH]                                  

12346 Created:2/28/2019              LIST:[ABC][DEF][GHI]          
      Updated:1/19/2021              [LMNO][PQRST]                 
      LIST:[ABC][DEF][GHI]                                         
      [LMNO][PQRST]                                                
      [SOC].[RAW]  

正则表达式匹配单个空格字符字符串的开头((\s|^)),然后是字符LIST:,后跟few连续,非- 句点字符(这将匹配空格和换行符,特别是)以允许匹配 - 以一个或多个空白字符继续,后跟一个单词(1个或多个非空白字符的字符串)和文字期间 (\.)。

我们必须返回的表达式用括号括起来,以便我们可以从regexp_substr 返回它。这种表达方式称为“捕获组”。正则表达式包含另一个捕获组(\s|^),出于必要(交替),因此我们必须返回的捕获组是正则表达式中的第二个。这就是 regexp_substr 的最后一个参数所做的:它指示函数返回第二个捕获组。

请注意句点的一个特殊之处(与括号表达式中转义的更一般概念有关):句点必须转义以表示正则表达式末尾的文字句点,而不是“任何字符” ;但是,在(否定的)括号表达式 [^.]*? 中,句点 - 表示文字句点,而不是“任何字符” - 被 not 转义。 Oracle 遵循 POSIX 标准的 ERE(扩展正则表达式)方言,该标准规定转义序列在括号表达式中无效。这与其他正则表达式方言不同,让很多用户感到困惑。

【讨论】:

    【解决方案2】:

    一种选择是使用REPLACE() 来删除line feed (CHR(10)) 和carriage return (CHR(13)),然后递归地使用REGEXP_REPLACE() 函数以提取LIST: 之后的子字符串点如

    SELECT col_a,
           'LIST:'||REGEXP_REPLACE(REPLACE(REPLACE(col_b,CHR(10)),CHR(13)),'(.*LIST:)(\S+)(\..*)','\2') AS result
      FROM t;
    
    col_a    result
    ------   -------
    12345    LIST:[ABC][DEF][GHI][LMNO][PQRST][Location=BLAH]
    12346    LIST:[ABC][DEF][GHI][LMNO][PQRST][SOC]    
    

    Demo

    【讨论】:

      【解决方案3】:

      可能有更有效的方法可以做到这一点,但以下似乎可以工作:

      首先我使用TRANSLATE 将换行符替换为空格,然后使用正则表达式查找LIST:. 之间的任何内容。然后我使用SUBSTRINSTR 删除最后的“单词”。我使用了子查询来避免重复第一步。

      SELECT
        SubQuery.COL_A,
        SUBSTR(SubQuery.WithWordAndDot, 1, INSTR(SubQuery.WithWordAndDot,' ',-1)-1) AS Result
      FROM 
      (
      SELECT
        COL_A,
        REGEXP_SUBSTR(TRANSLATE(COL_B, CHR(10)||CHR(13), ' '),'LIST:[^\.]+\.') as WithWordAndDot
      FROM MyTable
       ) SubQuery
       ;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-06-25
        • 1970-01-01
        • 1970-01-01
        • 2018-10-04
        • 2018-11-23
        • 1970-01-01
        • 1970-01-01
        • 2019-10-25
        相关资源
        最近更新 更多