【问题标题】:Extract text outside an opening and closing curly brace "{}" in PLSQL在 PLSQL 中提取左大括号“{}”之外的文本
【发布时间】:2021-11-24 06:40:03
【问题描述】:

我有一个文件模式,例如{user}_{yyyymmdd}_dailyreport.{type},我用它来提取左大括号和右大括号{}之间的值

with
     inputs (str) as (
       select '{user}_{yyyymmdd}_dailyreport.{type}'
       from   dual
     )
select level as ord,
substr(str, instr(str, '{', 1, level) + 1, instr(str, '}', 1, level) - instr(str, '{', 1, level) - 1) as data
from   inputs
connect by level <= length(str) - length(replace(str, '{'))
;

输出这个

ORD   DATA                                  
----- ----------
1     user                                   
2     yyyymmdd                               
3     type

在一个单独的列中,我想获得这个

ORD   STATIC SEGMENTS                                  
----- ----------
1     _                                   
2     _dailyreport.                                                       

但不确定如何获得。它基本上代表了没有包含在一对左大括号中的每个文本

【问题讨论】:

  • 大括号中的表达式是否总是出现在字符串的开头和结尾?花括号中的表达式是否总是由长度至少为一个的子字符串分隔?一般来说,返回所有不在大括号中的子字符串是最有意义的,包括在第一个 { 之前和最后一个 } 之后,以及大括号表达式之间,即使其中一些可能是空字符串 (@987654328 @ 在 Oracle 中)。单独的问题:你的字符串中是否也有“文字”花括号,也许以某种方式转义?
  • 大括号中的表达式是否总是出现在字符串的开头和结尾? - 是的。 {User} 将永远是第一个令牌,{Type} 将是最后一个。你的字符串中是否也有“文字”花括号 - 没有。
  • 字符串中间可能有其他标记,如{yyyymmdd}{country_code}等,以及字符串的固定部分(本例中为_dailyreport.),不一定在任何固定顺序。
  • 当我们最终得到一个遵循模式的文件名时(例如,在这种情况下为yorpa_20201001_dailyreport.json),我们希望通过静态段将它们映射到标记来提取信息。我们的最终输出(不在此问题的范围内,但在此处添加上下文)应该是1. user - yorpa 2. yyyymmdd - 20201001 3. type - json
  • 您的最新示例中没有任何花括号,那么它与您的问题有什么关系?

标签: sql regex oracle plsql


【解决方案1】:

如果 - 正如您在评论部分的回复中所说 - 初始和最终子字符串将始终是大括号表达式,这应该很容易。您正在寻找第 n 个右大括号和第 (n+1) 个左大括号之间的子字符串;所以你可以使用几乎相同的计算,并进行相应的修改。

with
     inputs (str) as (
       select '{user}_{yyyymmdd}_dailyreport.{type}'
       from   dual
     )
select level as ord,
substr(str, instr(str, '{', 1, level) + 1, 
       instr(str, '}', 1, level) - instr(str, '{', 1, level) - 1) as data,
substr(str, instr(str, '}', 1, level) + 1, 
       instr(str, '{', 1, level + 1) - instr(str, '}', 1, level) - 1) as static_segment
from   inputs
connect by level <= length(str) - length(replace(str, '{'))
;

ORD DATA       STATIC_SEGMENT    
--- ---------- ------------------
  1 user       _                 
  2 yyyymmdd   _dailyreport.     
  3 type                     

【讨论】:

    【解决方案2】:

    您可以使用递归子查询和简单的字符串函数来做到这一点(您不需要慢正则表达式):

    WITH bounds (str, start_pos, start_param, end_param, param_index) AS (
      SELECT str,
             1,
             INSTR(str, '{', 1),
             INSTR(str, '}', INSTR(str, '{', 1)),
             1
      FROM   inputs
    UNION ALL
      SELECT str,
             end_param + 1,
             INSTR(str, '{', end_param + 1),
             INSTR(str, '}', INSTR(str, '{', end_param + 1)),
             param_index + 1
      FROM   bounds
      WHERE  start_param > 0
      AND    end_param > 0
      AND    end_param < LENGTH(str)
    )
    SEARCH DEPTH FIRST BY str SET str_order
    SELECT str,
           param_index,
           CASE start_param
           WHEN 0
           THEN SUBSTR(str, start_pos)
           ELSE SUBSTR(str, start_pos, start_param - start_pos)
           END AS static_segment,
           CASE 
           WHEN start_param = 0 OR end_param = 0
           THEN NULL
           ELSE SUBSTR(str, start_param + 1, end_param - start_param - 1)
           END AS data
    FROM   bounds;
    

    其中,对于样本数据:

    CREATE TABLE inputs (str) AS
    SELECT '{user}_{yyyymmdd}_dailyreport.{type}' FROM DUAL UNION ALL
    SELECT 'abc{param1}def{param2}ghi{param3}jkl' FROM DUAL
    

    输出:

    STR PARAM_INDEX STATIC_SEGMENT DATA
    abc{param1}def{param2}ghi{param3}jkl 1 abc param1
    abc{param1}def{param2}ghi{param3}jkl 2 def param2
    abc{param1}def{param2}ghi{param3}jkl 3 ghi param3
    abc{param1}def{param2}ghi{param3}jkl 4 jkl
    {user}_{yyyymmdd}_dailyreport.{type} 1 user
    {user}_{yyyymmdd}_dailyreport.{type} 2 _ yyyymmdd
    {user}_{yyyymmdd}_dailyreport.{type} 3 _dailyreport. type

    db小提琴here

    【讨论】:

      【解决方案3】:

      使用regexp_substr 并将{} 视为分隔符,您需要第4 个单词:

      regexp_substr(str,'[^{}]+',1,4);
      

      【讨论】:

      • 嗨,如果问题不清楚,我很抱歉。我想要没有用大括号括起来的字符串的完整列表,而不仅仅是最后一个字符串。在这种情况下,__dailyreport. 都是相关的
      • 那么输出应该是包含值user,yyyymmdd,_dailyreport.,type的列吗?
      • 不,应该是__dailyreport.,因为这些文本不在一对花括号内。 useryyyymmddtype 都在花括号内。
      • 您是否真的需要遍历文件名,或者格式是否足够固定,您可以像我的示例中那样挑选出您想要的元素,只需更改最后一位?
      • 不确定“格式足够固定”是什么意思...我们只想要所有不被花括号括起来的字符串。
      【解决方案4】:

      帮助自己!如何?用另一个分隔符(例如分号)替换大括号;剩下的就很简单了。

      SQL> with
        2  inputs (str) as
        3    (select '{user}_{yyyymmdd}_dailyreport.{type}'
        4     from   dual
        5    ),
        6  replaced as
        7    (select trim(both ';' from replace(replace(str, '{', ';'), '}', ';')) val
        8     from inputs
        9    )
       10  select level ord,
       11         regexp_substr(val, '[^;]+', 1, level) result
       12  from replaced
       13  connect by level <= regexp_count(val, ';') + 1;
      
             ORD RESULT
      ---------- --------------------
               1 user
               2 _
               3 yyyymmdd
               4 _dailyreport.
               5 type
      
      SQL>
      

      【讨论】:

        猜你喜欢
        • 2010-09-27
        • 2011-01-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-14
        • 1970-01-01
        • 1970-01-01
        • 2019-03-18
        相关资源
        最近更新 更多