【问题标题】:ORA-00932 and ORA-22835 error on my SQL query我的 SQL 查询出现 ORA-00932 和 ORA-22835 错误
【发布时间】:2019-03-15 08:09:50
【问题描述】:

我有一个 SQL 表:

 CREATE TABLE "text_files"
( "FILE_NAME" VARCHAR2(4000 BYTE),
"FILE_CONTENT" CLOB
);

我正在尝试运行以下查询:

WITH rsqfc ( file_content, line, rn, max_rn, nm1, prv, depth ) AS (
  SELECT file_content,
         REGEXP_SUBSTR( file_content, '.+?(' || CHR(10) || '|$)', 1, 1 ),
         1,
         REGEXP_COUNT( file_content, '.+?(' || CHR(10) || '|$)' ) - 1,
         CASE SUBSTR( file_content, 1, 4 ) WHEN 'NM1*' THEN 1 ELSE 0 END,
         CASE SUBSTR( file_content, 1, 4 ) WHEN 'PRV*' THEN 1 ELSE 0 END,
         CASE SUBSTR( file_content, 1, 4 ) WHEN 'NM1*' THEN 1 WHEN 'PRV*' THEN 2 ELSE 0 END
  FROM   text_files
  UNION ALL
  SELECT file_content,
         REGEXP_SUBSTR( file_content, '.+?(' || CHR(10) || '|$)', 1, rn + 1 ),
         rn + 1,
         max_rn,
         CASE SUBSTR( REGEXP_SUBSTR( file_content, '.+?(' || CHR(10) || '|$)', 1, rn + 1 ), 1, 4 ) WHEN 'NM1*' THEN nm1 + 1 ELSE nm1 END,
         CASE SUBSTR( REGEXP_SUBSTR( file_content, '.+?(' || CHR(10) || '|$)', 1, rn + 1 ), 1, 4 ) WHEN 'PRV*' THEN prv + 1 ELSE prv END,
         CASE SUBSTR( REGEXP_SUBSTR( file_content, '.+?(' || CHR(10) || '|$)', 1, rn + 1 ), 1, 4 ) WHEN 'NM1*' THEN 1 WHEN 'PRV*' THEN 2 ELSE depth END
  FROM   rsqfc
  WHERE  rn < max_rn
)
SELECT CASE depth
       WHEN 2 THEN '2010A' || CHR( 64 + prv )
       WHEN 1 THEN '1000' || CHR( 64 + nm1 )
       WHEN 0 THEN '000'
       END AS "LOOP",
       line
FROM   rsqfc;

但是它给了

ORA-00932:不一致的数据类型:预期的 CLOB 得到 CHAR 错误

我了解我们不能将 CLOB 用于正则表达式和 where 子句。但是CLOB 中的内容超过 4000 个字符,当我尝试使用 VARCHAR2(4000) 字段更改表并将 CLOB 复制到新字段时,它不会复制。

它给了我

ORA-22835:缓冲区太小,无法进行 CLOB 到 CHAR 或 BLOB 到 RAW 转换(实际:4436,最大值:4000)

我无法将文本分成多个字段。我需要 1 个字段中的整个文本。

任何帮助将不胜感激。

【问题讨论】:

  • 不是解决方案,但这里也讨论了stackoverflow.com/questions/7357999/…。必须是 SQL 还是可以使用 pl/sql?
  • 应该是SQL。是否可以调整我的查询以支持它?
  • 我不认为你能在一列中获得超过 4000 个字符。 pl/sql 会给你 32K。
  • 但是您仍然可以创建一个 pl/sql 函数并从您的 select 语句中调用它。该函数将执行您的正则表达式处理,只要您不必返回超过 4000 个字节,您仍然可以使用 select 语句获取数据。这样可以吗?
  • 好的,我们试试吧。

标签: sql oracle


【解决方案1】:

使用流水线函数:

Oracle 设置 - 测试数据

CREATE TABLE text_files (
  FILE_NAME    VARCHAR2(4000 BYTE),
  FILE_CONTENT CLOB
);

INSERT INTO text_files VALUES (
  'test1',
  'ISA*00*AUTHORIZAT*00*SECURITY I*ZZ*000000060000000*ZZ*000000010000000*110705*1132*^*00501*110705001*0*T*:~
GS*HC*00000006*00000001*20110705*113253*110705001*X*005010X222A1~
ST*837*0021*005010X222~
BHT*0019*00*244579*20061015*1023*CH~
NM1*41*2*PREMIER BILLING SERVICE*****46*TGJ23~
PER*IC*JERRY*TE*3055552222*EX*231~
NM1*40*2*KEY INSURANCE COMPANY*****46*66783JJT~
HL*1**20*1~
PRV*BI*PXC*203BF0100Y~
NM1*85*2*BEN KILDARE SERVICE*****XX*9876543210~
N3*234 SEAWAY ST~
N4*MIAMI*FL*33111~'
);

Oracle 设置 - 流水线函数

CREATE TYPE line_detail AS OBJECT(
  line_no NUMBER(38,0),
  line    CLOB,
  loop    VARCHAR2(6)
);
/

CREATE TYPE line_detail_array AS TABLE OF line_detail;
/

CREATE FUNCTION get_Line_Detail(
  file_content IN CLOB
) RETURN line_detail_array PIPELINED
AS
  p_line       CLOB;
  p_loop       VARCHAR2(6) := '000';
  p_substr     CHAR(4);
  p_line_no    PLS_INTEGER := 0;
  p_line_start PLS_INTEGER := 1;
  p_line_end   PLS_INTEGER;
  p_line_len   PLS_INTEGER;
  p_length     PLS_INTEGER;
  p_nm1        PLS_INTEGER := 0;
  p_prv        PLS_INTEGER := 0;
BEGIN
  IF file_content IS NULL THEN
    RETURN;
  END IF;

  p_length := DBMS_LOB.GETLENGTH( file_content );

  LOOP
    p_line       := EMPTY_CLOB();
    p_line_no    := p_line_no + 1;
    p_substr     := DBMS_LOB.SUBSTR( file_content, 4, p_line_start );
    IF p_substr = 'PRV*' THEN
      p_prv := p_prv + 1;
      p_loop := '2010A' || CHR( 64 + p_prv );
    ELSIF p_substr = 'NM1*' THEN
      p_nm1 := p_nm1 + 1;
      p_loop := '1000' || CHR( 64 + p_nm1 );
    END IF;

    p_line_end   := DBMS_LOB.INSTR( file_content, CHR(10), p_line_start );
    IF p_line_end = 0 THEN
      p_line_end := p_length;
    END IF;

    LOOP
      EXIT WHEN p_line_start > p_line_end;
      p_line_len := LEAST( 4000, p_line_end - p_line_start + 1 );
      p_line := p_line || DBMS_LOB.SUBSTR( file_content, p_line_len, p_line_start );
      p_line_start := p_line_start + p_line_len;
    END LOOP;
    PIPE ROW( line_detail( p_line_no, p_line, p_loop ) );
    EXIT WHEN p_line_end >= p_length;
  END LOOP;
END;
/

查询

SELECT c.*
FROM   text_files t
       CROSS JOIN TABLE( get_Line_Detail( t.file_content ) ) c;

输出

LINE_NO |线 |环形 ------: | :------------------------------------------------ -------------------------------------------------- ---------- | :----- 1 | ISA*00*AUTHORIZAT*00*SECURITY I*ZZ*000000060000000*ZZ*000000010000000*110705*1132*^*00501*110705001*0*T*:~
| 000 2 | GS*HC*00000006*00000001*20110705*113253*110705001*X*005010X222A1~
| 000 3 | ST*837*0021*005010X222~
| 000 4 | BHT*0019*00*244579*20061015*1023*CH~
| 000 5 | NM1*41*2*高级计费服务*****46*TGJ23~
| 1000A 6 | PER*IC*JERRY*TE*3055552222*EX*231~
| 1000A 7 | NM1*40*2*KEY 保险公司*****46*66783JJT~
| 1000B 8 | HL*1**20*1~
| 1000B 9 | PRV*BI*PXC*203BF0100Y~
| 2010AA 10 | NM1*85*2*BEN KILDARE SERVICE*****XX*9876543210~
| 1000℃ 11 | N3*234 SEAWAY ST~
| 1000℃ 12 | N4*迈阿密*佛罗里达*33111~ | 1000℃

db小提琴here

【讨论】:

    【解决方案2】:

    假设文件的每一行都不会超过 4000 字节,那么:

    Oracle 设置

    CREATE TABLE text_files (
      FILE_NAME    VARCHAR2(4000 BYTE),
      FILE_CONTENT CLOB
    );
    
    INSERT INTO text_files VALUES (
      'test1',
      'ISA*00*AUTHORIZAT*00*SECURITY I*ZZ*000000060000000*ZZ*000000010000000*110705*1132*^*00501*110705001*0*T*:~
    GS*HC*00000006*00000001*20110705*113253*110705001*X*005010X222A1~
    ST*837*0021*005010X222~
    BHT*0019*00*244579*20061015*1023*CH~
    NM1*41*2*PREMIER BILLING SERVICE*****46*TGJ23~
    PER*IC*JERRY*TE*3055552222*EX*231~
    NM1*40*2*KEY INSURANCE COMPANY*****46*66783JJT~
    HL*1**20*1~
    PRV*BI*PXC*203BF0100Y~
    NM1*85*2*BEN KILDARE SERVICE*****XX*9876543210~
    N3*234 SEAWAY ST~
    N4*MIAMI*FL*33111~'
    );
    

    查询

    WITH rsqfc ( file_name, file_content, line_no, line, line_end, nm1, prv, depth ) AS (
      SELECT file_name,
             file_content,
             1,
             CASE
             WHEN DBMS_LOB.INSTR( file_content, CHR(10) ) = 0
             THEN DBMS_LOB.SUBSTR( file_content, DBMS_LOB.GETLENGTH( file_content ), 1 )
             ELSE DBMS_LOB.SUBSTR( file_content, DBMS_LOB.INSTR( file_content, CHR(10) ), 1 )
             END,
             DBMS_LOB.INSTR( file_content, CHR(10) ),
             CASE DBMS_LOB.SUBSTR( file_content, 4, 1 ) WHEN 'NM1*' THEN 1 ELSE 0 END,
             CASE DBMS_LOB.SUBSTR( file_content, 4, 1 ) WHEN 'PRV*' THEN 1 ELSE 0 END,
             CASE DBMS_LOB.SUBSTR( file_content, 4, 1 ) WHEN 'NM1*' THEN 1 WHEN 'PRV*' THEN 2 ELSE 0 END
      FROM   text_files
      UNION ALL
      SELECT file_name,
             file_content,
             line_no + 1,
             CASE
             WHEN DBMS_LOB.INSTR( file_content, CHR(10), line_end + 1 ) = 0
             THEN DBMS_LOB.SUBSTR(
                    file_content,
                    DBMS_LOB.GETLENGTH( file_content ) - line_end,
                    line_end + 1
                  )
             ELSE DBMS_LOB.SUBSTR(
                    file_content,
                    DBMS_LOB.INSTR( file_content, CHR(10), line_end + 1 ) - line_end,
                    line_end + 1
                  )
             END,
             DBMS_LOB.INSTR( file_content, CHR(10), line_end + 1 ),
             CASE DBMS_LOB.SUBSTR( file_content, 4, line_end + 1 ) WHEN 'NM1*' THEN nm1 + 1 ELSE nm1 END,
             CASE DBMS_LOB.SUBSTR( file_content, 4, line_end + 1 ) WHEN 'PRV*' THEN prv + 1 ELSE prv END,
             CASE DBMS_LOB.SUBSTR( file_content, 4, line_end + 1 ) WHEN 'NM1*' THEN 1 WHEN 'PRV*' THEN 2 ELSE depth END
      FROM   rsqfc
      WHERE  line_end > 0
    )
    SELECT line_no,
           CASE depth
           WHEN 2 THEN '2010A' || CHR( 64 + prv )
           WHEN 1 THEN '1000' || CHR( 64 + nm1 )
           WHEN 0 THEN '000'
           END AS "LOOP",
           line
    FROM   rsqfc;
    

    输出

    LINE_NO |循环 |线 ------: | :----- | :------------------------------------------------ -------------------------------------------------- ---------- 1 | 000 | ISA*00*AUTHORIZAT*00*SECURITY I*ZZ*000000060000000*ZZ*000000010000000*110705*1132*^*00501*110705001*0*T*:~
    2 | 000 | GS*HC*00000006*00000001*20110705*113253*110705001*X*005010X222A1~
    3 | 000 | ST*837*0021*005010X222~
    4 | 000 | BHT*0019*00*244579*20061015*1023*CH~
    5 | 1000A | NM1*41*2*高级计费服务*****46*TGJ23~
    6 | 1000A | PER*IC*JERRY*TE*3055552222*EX*231~
    7 | 1000B | NM1*40*2*KEY 保险公司*****46*66783JJT~
    8 | 1000B | HL*1**20*1~
    9 | 2010AA | PRV*BI*PXC*203BF0100Y~
    10 | 1000℃ | NM1*85*2*BEN KILDARE SERVICE*****XX*9876543210~
    11 | 1000℃ | N3*234 SEAWAY ST~
    12 | 1000℃ | N4*迈阿密*佛罗里达*33111~

    db小提琴here

    【讨论】:

    • 恐怕有几个值超过了 4000。如何处理?
    • @dang 使用流水线 PL/SQL 函数进行处理。
    • 我不知道如何为这样的事情做流水线化的 pl/sql。
    【解决方案3】:

    那么这是它的存根..

    create or replace function get_depth(sFileName in VARCHAR2)
    return varchar2 as
        sBuffer varchar2(32000);
        iResult integer := 0;
    begin
        --  
        for rec in (select file_content from "text_files" where file_name = sFileName) loop
              sBuffer := dbms_lob.substr(rec.file_content,32000);
              exit;
        end loop;
        if sBuffer is not null then
        -- Your regexp on sBuffert
    
    
    
        end if;
        return iResult;
    end get_depth;
    

    然后选择:

    select get_depth('myfile') depth from dual;
    

    【讨论】:

      猜你喜欢
      • 2016-07-07
      • 1970-01-01
      • 1970-01-01
      • 2012-07-28
      • 2017-09-28
      • 2022-01-06
      • 2016-11-25
      • 1970-01-01
      • 2022-01-02
      相关资源
      最近更新 更多