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

时间:2019-04-03 12:07:54

标签: sql oracle stored-procedures oracle-sqldeveloper

我正试图为数据类型为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太大了。

3 个答案:

答案 0 :(得分: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 :(得分: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行仍会以{{ 1}}无法解决这个问题。

您可以通过在数字变量周围使用dbms_output或使用to_char()而不是串联来避免第一个问题:

dbms_lob.append

只要XML值包含换行符,就可以避免第二个问题,方法是将CLOB分成几行as shown here,但要稍作修改以处理空行。带有附加变量声明为:

...
      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;
      /');
...

然后而不是:

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

您可以这样做:

  DBMS_OUTPUT.PUT_LINE(V_BLOCK);

db<>fiddle


@VinayakDwivedi编辑后添加了要使用的功能:

  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;

...但是这将导致每10000个字符额外的换行符。


但是,值得注意的是,您在其中生成的PL / SQL块以如下行结尾:

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;

,如果运行了该生成的代码,并且原始XML大于32k,也会出错。确实,生成的代码也需要分解才能以块的形式重建您的CLOB-即一个循环,一次要占用32k并连接/附加这些块以重新构成完整值。它在每行的开头也有空格,因此V_XML := '<original xml from table>'; 等,更重要的是最后的DECLARE不在其各自行的开头,这在尝试按以下方式运行它时也会引起问题-是。

答案 2 :(得分:0)

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

  1. 数字大-我怀疑这是否是您的问题,因为最大数字非常大
  2. 转换错误-您正在尝试将非数字转换为数字
  3. 将NULL分配给NOT NULL约束变量-自移植

在不了解更多上下文的情况下,实际上不可能确定其中哪个是您的问题。

希望这会有所帮助!