Невозможно получить большие данные, т.е. XML из оракула, используя процедуру - PullRequest
0 голосов
/ 03 апреля 2019

Я пытаюсь создать оператор обновления для столбца с типом данных 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 ]

2 голосов
/ 03 апреля 2019

Ну, я придумала контрольный пример, чтобы воспроизвести это (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;
/

Проблема, похоже, заключается в том, что когда вы объединяете строки с каналами, Oracle может добавлять 2 сгустка вместе, если один из них превышает 32 КБ, и он может неявно преобразовывать varchar2 в сгусток и добавлять их. Но если вы попытаетесь добавить число к 32-килобайтному сбою, это не удастся. Он понимает, как добавить varchar2 и число, и clob и clob, и clob и varchar2. Но он не может автоматически понять, как сделать число -> varchar2 -> clob. Вы можете исправить это, заключив строку в to_clob(), избегая проблемы с неявным преобразованием Oracle.

1 голос
/ 03 апреля 2019

Где вы получите ошибку, зависит от длины XML значения CLOB вашей таблицы.

Если XML больше 32 КБ, вы увидите ошибку в строке 27 в своем коде, от попыткиобъединить a varchar2 string число (как продемонстрировано @kfinity) в CLOB;поведение, которое не объяснено в документации , но предположительно связано с неявным преобразованием, поскольку просто явно конвертирует числа с помощью 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 на строки, , как показано здесь , но с небольшой модификацией для обработки пустых строк;с дополнительными переменными, объявленными как:

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больше 32к.Действительно, этот сгенерированный код также необходимо разбить, чтобы реконструировать ваш CLOB по частям - то есть цикл, который принимает 32 КБ за раз и объединяет / добавляет эти куски, чтобы восстановить полное значение.Он также имеет пробел в начале каждой строки, поэтому DECLARE и т. Д. И, что более важно, конечный / не в начале соответствующих строк, что также вызовет проблемы при попытке запустить его как есть.

0 голосов
/ 03 апреля 2019

Выезд: https://www.techonthenet.com/oracle/errors/ora06502.php Это говорит о том, что возможны 3 возможные причины этой ошибки

  1. Число к большому - я сомневаюсь, что это ваша проблема, так как максимальное число довольно велико
  2. Ошибка преобразования - вы пытаетесь преобразовать не число в число
  3. Присвоение NULL переменной с ограничением NOT NULL - само объяснение

, не зная больше контекста, на самом деле невозможно определить, какая из них является вашей проблемой.

Надеюсь, это поможет!

...