无法使用过程从oracle表获取大数据,即XML

问题描述 投票:0回答:3

我想为数据类型为CLOB的列创建更新语句。为此,我从表中获取XML并将其写入oracle控制台供以后使用。对于某些数据,它工作正常。对于一些人我得到错误

例外1:CREATE_UPDATE_XML_QUERY('600264','700009');

--ORA-06502:PL / SQL:数字或值错误

我改变了V_XML的数据类型,V_BLOCK没有任何效果

PROCEDURE CREATE_UPDATE_XML_QUERY
(
 MY_ID          NUMBER,
 MY_ID2       NUMBER
) AS
V_SCREEN_VERSION  NUMBER;
V_XML_ID          NUMBER;
V_CNT NUMBER;
V_XML CLOB);
V_BLOCK CLOB;
BEGIN
      SELECT XML,XMLID INTO V_XML,V_XML_ID 
      FROM XML_TABLE WHERE ENC_ID = MY_ID AND SCREEN_ID = MY_ID2 ; ----getting excption

      V_BLOCK := 
      '
      SET SERVEROUTPUT ON;
      DECLARE 
      V_XML CLOB ;

      BEGIN 

      ';

      V_BLOCK := V_BLOCK||'V_XML := '''||V_XML||''';';

      V_BLOCK := V_BLOCK||'

      UPDATE XML_TABLE SET XML = '||'V_XML'||'
      WHERE ENC_ID = '||MY_ID||' AND ENC_TYPE = ''P'' AND SCREEN_ID = '||MY_ID2||' AND XMLID = '||V_XML_ID||';
      --DBMS_OUTPUT.PUT_LINE(''V_XML =>''||V_XML);
      DBMS_OUTPUT.PUT_LINE(''ROWCOUNT =>''||SQL%ROWCOUNT);

      END;
      /';
      DBMS_OUTPUT.PUT_LINE('--Printing Annomous Block the XML :->>');


      DBMS_OUTPUT.PUT_LINE(V_BLOCK);
  EXCEPTION 
  WHEN OTHERS THEN
  DBMS_OUTPUT.PUT_LINE('Exception1 : UPDATE_SCREEN_MASTER_XML('''||MY_ID||''','''||MY_ID2||''','''||V_XML_ID||'''); --'||SQLERRM);--'||SQLERRM);

END CREATE_UPDATE_XML_QUERY;

我怎样才能避免错误。是因为我的XML太大了。

sql oracle stored-procedures oracle-sqldeveloper
3个回答
2
投票

好吧,我已经提出了一个测试用例来重现这个(Oracle 12.2.0.1),你说对了,问题不在于DBMS_OUTPUT行。

declare
  v_clob clob;
  xmlid number;
begin
  -- initialize clob and make clob a string of length 32768
  dbms_lob.createtemporary(v_clob, true);
  for i in 1..32768 loop
    v_clob := v_clob || 'x';
  end loop;
  dbms_output.put_line(length(v_clob));
  -- testing:
  v_clob := v_clob || 'x'; -- appending a varchar2 works fine
  v_clob := v_clob || xmlid; -- appending a number gives ORA-06502
  v_clob := v_clob || 'x' || xmlid; -- appending a string+number still gives ORA-06502
  v_clob := v_clob || to_clob(xmlid); -- works fine
  dbms_lob.append(v_clob, 'x' || xmlid); -- also works fine
  dbms_output.put_line(length(v_clob));
  dbms_output.put_line(substr(v_clob,1,32767));
end;
/

问题似乎是当你用管道连接字符串时,如果其中一个超过32k,Oracle可以将2个clob附加在一起,并且它可以隐式地将varchar2转换为clob并附加它们。但是如果你试图将数字附加到超过32k的clob上,它就会失败。它了解如何附加varchar2和number,以及clob和clob以及clob和varchar2。但它似乎无法自动弄清楚如何做数字 - > varchar2 - > clob。您可以通过将字符串包装在to_clob()中来解决此问题,从而避免Oracle隐式转换的问题。


1
投票

你得到的错误取决于你的表的XML CLOB值的长度。

如果XML超过32k,那么您将在代码中的第27行看到错误,从尝试连接 一个varchar2字符串 一个数字(如@kfinity所示)到CLOB上;行为没有解释in the documentation,但可能与隐式转换有关,因为只是用to_char(MY_ID)(或to_clob(MY_ID))明确转换数字。

如果XML小于,但接近32k,那么你只需要超过它,但V_BLOCK CLOB仍然最终大于32k,然后仍然会在第39行出错,因为dbms_output无法处理。

您可以通过在数字变量周围使用to_char()或使用dbms_lob.append而不是连接来避免第一个问题:

...
      V_BLOCK := 
      '
      SET SERVEROUTPUT ON;
      DECLARE 
      V_XML CLOB ;

      BEGIN 

      ';

      dbms_lob.append(V_BLOCK, 'V_XML := '''||V_XML||''';');

      dbms_lob.append(V_BLOCK, '

      UPDATE XML_TABLE SET XML = '||'V_XML'||'
      WHERE ENC_ID = '||MY_ID||' AND ENC_TYPE = ''P'' AND SCREEN_ID = '||MY_ID2||' AND XMLID = '||V_XML_ID||';
      --DBMS_OUTPUT.PUT_LINE(''V_XML =>''||V_XML);
      DBMS_OUTPUT.PUT_LINE(''ROWCOUNT =>''||SQL%ROWCOUNT);

      END;
      /');
...

并且你可以避免第二个问题,只要你的XML值包含换行符,通过将CLOB分成行as shown here,但稍微修改一下来处理空行;其他变量声明为:

V_BUFFER VARCHAR2(32767);
V_AMOUNT PLS_INTEGER;
V_POS PLS_INTEGER := 1;

而不是:

  DBMS_OUTPUT.PUT_LINE(V_BLOCK);

你可以做:

  WHILE V_POS < length(V_BLOCK) LOOP
    -- read to next newline if there is one, rest of CLOB if not
    IF dbms_lob.instr(V_BLOCK, chr(10), V_POS) > 0 THEN
      V_AMOUNT := dbms_lob.instr(V_BLOCK, chr(10), V_POS) - V_POS;
      IF V_AMOUNT = 0 THEN
        V_BUFFER := null; -- first character is a new line (i.e. a blank line)
      ELSE 
        dbms_lob.read(V_BLOCK, V_AMOUNT, V_POS, V_BUFFER);
      END IF;
      V_POS := V_POS + V_AMOUNT + 1; -- skip newline character
    ELSE
      V_AMOUNT := 32767;
      dbms_lob.read(V_BLOCK, V_AMOUNT, V_POS, V_BUFFER);
      V_POS := V_POS + V_AMOUNT;
    END IF;

    DBMS_OUTPUT.PUT_LINE(V_BUFFER);
  END LOOP;

db<>fiddle


@VinayakDwivedi编辑添加要使用的函数:

PROCEDURE print_clob_to_output (p_clob IN CLOB)
IS
    v_offset       NUMBER := 1;
    v_chunk_size   NUMBER := 10000;
BEGIN
    LOOP
        EXIT WHEN v_offset > DBMS_LOB.getlength (p_clob);
        DBMS_OUTPUT.put_line (
            DBMS_LOB.SUBSTR (p_clob, v_chunk_size, v_offset));
        v_offset := v_offset + v_chunk_size;
    END LOOP;
END print_clob_to_output;

...但这会每10000个字符引入额外的换行符。


但是,值得注意的是,您在其中生成的PL / SQL块最终会产生如下行:

V_XML := '<original xml from table>';

如果生成的代码运行,如果原始XML超过32k,它也会出错。实际上,生成的代码也需要分解,以便在块中重建CLOB - 即一次占用32k的循环并连接/附加这些块以重建完整值。它在每一行的开头都有空格,所以DECLARE等等,更重要的是最终的/不是它们各自行的开头,这也会在尝试按原样运行时引起问题。


0
投票

退房:https://www.techonthenet.com/oracle/errors/ora06502.php这表明此错误可能有3种可能的原因

  1. 数字到大 - 我怀疑这是否是你的问题,因为最大数字是非常巨大的
  2. 转换错误 - 您尝试将非数字转换为数字
  3. 为NOT NULL约束变量赋值NULL - 自我解释

如果不了解更多上下文,则无法确定哪些是您的问题。

希望这可以帮助!

© www.soinside.com 2019 - 2024. All rights reserved.