【问题标题】:Extract TEXT from a CLOB field从 CLOB 字段中提取 TEXT
【发布时间】:2015-04-08 12:03:35
【问题描述】:

我的 Oracle 数据库中有一个 CLOB 字段,它以以下格式存储 TEXT 数据:

__99__RU_LOCKED=N;;__99__RU_SUSPENDED=Y;;__17__USER_TYPE=A;;__17__USER_TYPE_610=A;;__17__GUIFLAG=0;;__17__DEFAULT_LANG_610=E;;__17__OUTPUT_DEVICE_46=LOCL;;__17__PRINT_IMMED=G;;__17__DELETE_AFTER_PRINT=D;;__17__CATT=*BLANK;;__17__CATT_46=*;;__17__DEC_FORMAT=*BLANK;;__17__DEC_FORMAT_46=X;;__17__DATE_FORMAT=2;;__17__PARAMETERS=OM_OBJM_NO_DISPLAYX;;__17__MEAS_EASLPFL=0;;__17__USER_GROUP=S1BR22;;__17__VALID_FROM=20080222;;__17__VALID_UNTIL=99991231;;__17__ACCOUNT=37004968;;

我正在使用 TOAD,在创建查询时,我可以使用以下内容读取 CLOB 字段:

--- To read the CLOB field.
select DBMS_LOB.substr(ADD_INFO_MASTER) from USER

此选择将 CLOB 字段返回给我。

我的问题是:有没有办法从上面的行中提取一个单一的值,比如 ACCOUNT 值? 请记住,这个 CLOB 字段可以变化,并且 __17__ACCOUNT= 不会每次都在同一个地方。我需要一种提取方法来定位 ;;__17__ACCOUNT=(这将是一个模式)并提取值 37004968。

在 TOAD 中执行查询时可以实现这一点吗?

【问题讨论】:

    标签: oracle extract toad clob


    【解决方案1】:

    如果要处理大于 4000 个符号长度 (Oracle 11g) 或 32K 长度 (Oracle 12c) 的 CLOB 值,则必须使用 DBMS_LOB package

    此包包含在 LOB 上运行的 instr()substr() 函数。

    在您的情况下,查询如下所示:

    with prm as (
      select '__17__ACCOUNT' as fld_start from dual
    )
    select 
      dbms_lob.substr(
        text,         
        -- length of substring             
        (  
          -- position of delimiter found after start of desired field 
          dbms_lob.instr(text, ';;', dbms_lob.instr(text, prm.fld_start)) 
    
          -
    
          -- position of the field description plus it's length
          ( dbms_lob.instr(text, prm.fld_start) + length(fld_start) + 1 )
        ),
    
        -- start position of substring
        dbms_lob.instr(text,prm.fld_start) + length(fld_start) + 1
      )  
    from 
      text_table,
      prm
    

    上面的查询使用这个设置:

    create table text_table(text clob);
    
    insert into text_table(text) values (
      '__99__RU_LOCKED=N;;__99__RU_SUSPENDED=Y;;__17__USER_TYPE=A;;__17__USER_TYPE_610=A;;__17__GUIFLAG=0;;__17__DEFAULT_LANG_610=E;;__17__OUTPUT_DEVICE_46=LOCL;;__17__PRINT_IMMED=G;;__17__DELETE_AFTER_PRINT=D;;__17__CATT=*BLANK;;__17__CATT_46=*;;__17__DEC_FORMAT=*BLANK;;__17__DEC_FORMAT_46=X;;__17__DATE_FORMAT=2;;__17__PARAMETERS=OM_OBJM_NO_DISPLAYX;;__17__MEAS_EASLPFL=0;;__17__USER_GROUP=S1BR22;;__17__VALID_FROM=20080222;;__17__VALID_UNTIL=99991231;;__17__ACCOUNT=37004968;;'
    );  
    

    对于开发工具的日常使用,定义一个返回所需名称的字段值的函数并使用它而不是每次都编写复杂的表达式可能很有用。
    例如。 :

    create or replace function get_field_from_text(
      pi_text       in clob,
      pi_field_name in varchar2
    ) return varchar2 deterministic parallel_enable
    is
      v_start_pos   binary_integer;
      v_field_start varchar2(4000);
      v_field_value varchar2(32767);
    begin
    
      if( (pi_text is null) or (pi_field_name is null) ) then
        return null;
      end if;
    
      v_field_start := pi_field_name || '=';
      v_start_pos := dbms_lob.instr(pi_text, v_field_start);
    
      if(v_start_pos is null) then
        return null;
      end if;
    
      v_start_pos := v_start_pos + length(v_field_start);
    
      v_field_value := dbms_lob.substr(
                         pi_text,
                         (dbms_lob.instr(pi_text, ';;', v_start_pos) - v_start_pos),
                         v_start_pos
                       );
    
      return v_field_value;
    end;
    

    用法:

    select get_field_from_text(text,'__17__OUTPUT_DEVICE_46') from text_table
    

    【讨论】:

      【解决方案2】:

      您可以使用正则表达式来提取值:

      WITH your_table AS (
      SELECT '__99__RU_LOCKED=N;;__99__RU_SUSPENDED=Y;;__17__USER_TYPE=A;;__17__USER_TYPE_610=A;;__17__GUIFLAG=0;;__17__DEFAULT_LANG_610=E;;__17__OUTPUT_DEVICE_46=LOCL;;__17__PRINT_IMMED=G;;__17__DELETE_AFTER_PRINT=D;;__17__CATT=*BLANK;;__17__CATT_46=*;;__17__DEC_FORMAT=*BLANK;;__17__DEC_FORMAT_46=X;;__17__DATE_FORMAT=2;;__17__PARAMETERS=OM_OBJM_NO_DISPLAYX;;__17__MEAS_EASLPFL=0;;__17__USER_GROUP=S1BR22;;__17__VALID_FROM=20080222;;__17__VALID_UNTIL=99991231;;__17__ACCOUNT=37004968;;' clob_field FROM DUAL
      ) 
      SELECT REGEXP_SUBSTR(clob_field,'__17__ACCOUNT=.*;;')
      FROM your_table
      

      使用它你会得到“__17__ACCOUNT=37004968;;”。您可以使用 SUBSTR 轻松提取值。

      我认为在 Oracle 11g REGEXP_SUBSTR 中有额外的参数可以让您在正则表达式中提取特定组。

      【讨论】:

        【解决方案3】:

        您可以将 INSTR 和 SUBSTR 与 CLOB 数据类型一起使用:

        WITH T1 AS (
        SELECT '__99__RU_LOCKED=N;;__99__RU_SUSPENDED=Y;;__17__USER_TYPE=A;;__17__USER_TYPE_610=A;;__17__GUIFLAG=0;;__17__DEFAULT_LANG_610=E;;__17__OUTPUT_DEVICE_46=LOCL;;__17__PRINT_IMMED=G;;__17__DELETE_AFTER_PRINT=D;;__17__CATT=*BLANK;;__17__CATT_46=*;;__17__DEC_FORMAT=*BLANK;;__17__DEC_FORMAT_46=X;;__17__DATE_FORMAT=2;;__17__PARAMETERS=OM_OBJM_NO_DISPLAYX;;__17__MEAS_EASLPFL=0;;__17__USER_GROUP=S1BR22;;__17__VALID_FROM=20080222;;__17__VALID_UNTIL=99991231;;__17__ACCOUNT=37004968;;' TEXT FROM DUAL
        ) 
        SELECT SUBSTR(TEXT, 
                      INSTR(TEXT, '__17__ACCOUNT=') + LENGTH('__17__ACCOUNT') + 1, -- find the first position of the value
                      INSTR (TEXT, ';;', INSTR(TEXT, '__17__ACCOUNT=')) - (INSTR(TEXT, '__17__ACCOUNT=') + LENGTH('__17__ACCOUNT') + 1)  -- length to read. Difference between the end position (the first ;; after your placeholder) and the value start position (the same value as above)
                     )
          FROM T1;
        

        不过我更喜欢 pablomatico 提出的 REGEXP 解决方案。

        【讨论】:

          猜你喜欢
          • 2017-06-15
          • 2015-12-21
          • 2023-03-09
          • 2014-01-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-25
          • 2015-09-08
          相关资源
          最近更新 更多