Где вы получите ошибку, зависит от длины 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
и т. Д. И, что более важно, конечный /
не в начале соответствующих строк, что также вызовет проблемы при попытке запустить его как есть.