Oracle PL SQL 在处理过程中的最后一条语句后,过程需要很长时间才能完成
Oracle PL SQL Procedure takes long time to complete, after the very last statement in the procedure is processed
我们正在使用 Oracle 11.2.0.4.0。
我们创建了一个过程来为所有可用行更新 table 中的 CLOB 列。
我们有一个 DBMS_OUTPUT.PUT_LINE 语句在过程的最后一行打印 SYSDATE。
当我们执行这个程序时,在最后一行打印 SYSDATE 之后需要很长时间才能完成。
不确定我是否遗漏了什么。任何帮助将不胜感激
我们尝试通过在过程中使用 DBMS_OUPUT.PUT_LINE(SYSDATE)
语句来执行该过程,以 debug/find 痛点。
这就是我们执行过程并将输出粘贴到最后的方式:
SET TIMING ON
SET SERVEROUTPUT ON
SELECT 'Outside block Start time: ' || to_char(SYSDATE, 'DD-MON-YYYY HH24:MI:SS') OUTSIDEBLOCKSTARTTIME from dual;
DECLARE
I_LIMIT BINARY_INTEGER;
I_STR_TO_REPLACE VARCHAR2(200);
I_REPLACEMENT_STR VARCHAR2(200);
BEGIN
I_LIMIT := 10000;
I_STR_TO_REPLACE := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
I_REPLACEMENT_STR := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
P_TEST_CLOB_MASK(
I_LIMIT => I_LIMIT,
I_STR_TO_REPLACE => I_STR_TO_REPLACE,
I_REPLACEMENT_STR => I_REPLACEMENT_STR
);
END;
/
SELECT 'Outside block End time: ' || to_char(SYSDATE, 'DD-MON-YYYY HH24:MI:SS') OUTSIDEBLOCKENDTIME from dual;
以下是程序的代码:
CREATE OR REPLACE PROCEDURE p_test_clob_mask (
i_limit IN PLS_INTEGER DEFAULT 1000,
i_str_to_replace IN VARCHAR2
DEFAULT 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890',
i_replacement_str IN VARCHAR2
DEFAULT 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
)
IS
CURSOR cur_fetch_cddaddcomphistory
IS
SELECT ROWID row_id, ua4cf_cddaddcomphistory ua4cf_cddaddcomphistory
FROM generic_set11
WHERE ua4cf_cddaddcomphistory IS NOT NULL;
l_limit NUMBER := 0;
l_counter NUMBER := 0;
l_rowid ROWID;
l_hold_clob CLOB;
l_masked_clob CLOB;
l_length_clob NUMBER := 0;
l_str_to_replace VARCHAR2 (500);
l_replacement_str VARCHAR2 (500);
l_overall_start_dt DATE;
l_batch_start_dt DATE := NULL;
l_batch_time_taken NUMBER := 0;
l_overall_time_taken NUMBER := 0;
BEGIN
l_overall_start_dt := SYSDATE;
DBMS_OUTPUT.put_line ( 'Overall Start: '
|| TO_CHAR (l_overall_start_dt,
'DD-MON-RR HH24:MI:SS'
)
);
l_str_to_replace := i_str_to_replace;
l_replacement_str := i_replacement_str;
l_limit := i_limit;
OPEN cur_fetch_cddaddcomphistory;
LOOP
FETCH cur_fetch_cddaddcomphistory
INTO l_rowid, l_hold_clob;
EXIT WHEN cur_fetch_cddaddcomphistory%NOTFOUND;
l_counter := l_counter + 1;
l_length_clob := DBMS_LOB.getlength (l_hold_clob);
DBMS_LOB.createtemporary (l_masked_clob, TRUE);
DBMS_LOB.writeappend (l_masked_clob,
l_length_clob,
TRANSLATE (l_hold_clob,
i_str_to_replace,
i_replacement_str
)
);
UPDATE generic_set11
SET ua4cf_cddaddcomphistory = l_masked_clob
WHERE ROWID = l_rowid;
IF MOD (l_counter, l_limit) = 0
THEN
COMMIT;
END IF;
END LOOP;
CLOSE cur_fetch_cddaddcomphistory;
COMMIT;
l_overall_time_taken :=
TRUNC (((SYSDATE - l_overall_start_dt) * 24 * 60), 2);
DBMS_OUTPUT.put_line ( 'Overall End: '
|| TO_CHAR (SYSDATE, 'DD-MON-RR HH24:MI:SS')
|| ' - Time taken in Mins: '
|| l_overall_time_taken
|| ' - Counters: '
|| l_counter
);
END p_test_clob_mask;
/
我们的期望是在最后一个 DBMS_OUPUT.PUT_LINE
完成后,程序应该完成。
但是经过的时间显示 20 分钟以上,而根据最后的 DBMS_OUPUT.PUT_LINE
声明,所用时间仅为 3 分钟
下面是输出:
OUTSIDEBLOCKSTARTTIME
-------------------------------------------------------
Outside block Start time: 27-DEC-2018 15:33:45
Elapsed: 00:00:00.118
Overall Start: 27-DEC-18 15:33:45
Overall End: 27-DEC-18 15:37:37 - Time taken in Mins: 3.86 - Counters: 138913
PL/SQL procedure successfully completed.
Elapsed: 00:20:19.313
OUTSIDEBLOCKENDTIME
-----------------------------------------------------
Outside block End time: 27-DEC-2018 15:54:04
Elapsed: 00:00:00.095
根据评论,修复是添加行
DBMS_LOB.freetemporary(l_masked_clob);
在循环结束时。这确保使用的临时 CLOB
s 在使用时被清理。编写的代码导致 Oracle 为循环的每次迭代创建一个新的临时 CLOB
,并且很可能清除所有这些临时 CLOB
(可能有数百甚至数千个)在块的末尾导致延迟。
另一种可能的性能提升可以通过在循环外创建一个临时 LOB
并在每次迭代结束时将其清除(例如使用 DBMS_LOB.erase
或 DBMS_LOB.trim
)来获得循环,并在最后释放它。
我们正在使用 Oracle 11.2.0.4.0。 我们创建了一个过程来为所有可用行更新 table 中的 CLOB 列。
我们有一个 DBMS_OUTPUT.PUT_LINE 语句在过程的最后一行打印 SYSDATE。
当我们执行这个程序时,在最后一行打印 SYSDATE 之后需要很长时间才能完成。
不确定我是否遗漏了什么。任何帮助将不胜感激
我们尝试通过在过程中使用 DBMS_OUPUT.PUT_LINE(SYSDATE)
语句来执行该过程,以 debug/find 痛点。
这就是我们执行过程并将输出粘贴到最后的方式:
SET TIMING ON
SET SERVEROUTPUT ON
SELECT 'Outside block Start time: ' || to_char(SYSDATE, 'DD-MON-YYYY HH24:MI:SS') OUTSIDEBLOCKSTARTTIME from dual;
DECLARE
I_LIMIT BINARY_INTEGER;
I_STR_TO_REPLACE VARCHAR2(200);
I_REPLACEMENT_STR VARCHAR2(200);
BEGIN
I_LIMIT := 10000;
I_STR_TO_REPLACE := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
I_REPLACEMENT_STR := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
P_TEST_CLOB_MASK(
I_LIMIT => I_LIMIT,
I_STR_TO_REPLACE => I_STR_TO_REPLACE,
I_REPLACEMENT_STR => I_REPLACEMENT_STR
);
END;
/
SELECT 'Outside block End time: ' || to_char(SYSDATE, 'DD-MON-YYYY HH24:MI:SS') OUTSIDEBLOCKENDTIME from dual;
以下是程序的代码:
CREATE OR REPLACE PROCEDURE p_test_clob_mask (
i_limit IN PLS_INTEGER DEFAULT 1000,
i_str_to_replace IN VARCHAR2
DEFAULT 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890',
i_replacement_str IN VARCHAR2
DEFAULT 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
)
IS
CURSOR cur_fetch_cddaddcomphistory
IS
SELECT ROWID row_id, ua4cf_cddaddcomphistory ua4cf_cddaddcomphistory
FROM generic_set11
WHERE ua4cf_cddaddcomphistory IS NOT NULL;
l_limit NUMBER := 0;
l_counter NUMBER := 0;
l_rowid ROWID;
l_hold_clob CLOB;
l_masked_clob CLOB;
l_length_clob NUMBER := 0;
l_str_to_replace VARCHAR2 (500);
l_replacement_str VARCHAR2 (500);
l_overall_start_dt DATE;
l_batch_start_dt DATE := NULL;
l_batch_time_taken NUMBER := 0;
l_overall_time_taken NUMBER := 0;
BEGIN
l_overall_start_dt := SYSDATE;
DBMS_OUTPUT.put_line ( 'Overall Start: '
|| TO_CHAR (l_overall_start_dt,
'DD-MON-RR HH24:MI:SS'
)
);
l_str_to_replace := i_str_to_replace;
l_replacement_str := i_replacement_str;
l_limit := i_limit;
OPEN cur_fetch_cddaddcomphistory;
LOOP
FETCH cur_fetch_cddaddcomphistory
INTO l_rowid, l_hold_clob;
EXIT WHEN cur_fetch_cddaddcomphistory%NOTFOUND;
l_counter := l_counter + 1;
l_length_clob := DBMS_LOB.getlength (l_hold_clob);
DBMS_LOB.createtemporary (l_masked_clob, TRUE);
DBMS_LOB.writeappend (l_masked_clob,
l_length_clob,
TRANSLATE (l_hold_clob,
i_str_to_replace,
i_replacement_str
)
);
UPDATE generic_set11
SET ua4cf_cddaddcomphistory = l_masked_clob
WHERE ROWID = l_rowid;
IF MOD (l_counter, l_limit) = 0
THEN
COMMIT;
END IF;
END LOOP;
CLOSE cur_fetch_cddaddcomphistory;
COMMIT;
l_overall_time_taken :=
TRUNC (((SYSDATE - l_overall_start_dt) * 24 * 60), 2);
DBMS_OUTPUT.put_line ( 'Overall End: '
|| TO_CHAR (SYSDATE, 'DD-MON-RR HH24:MI:SS')
|| ' - Time taken in Mins: '
|| l_overall_time_taken
|| ' - Counters: '
|| l_counter
);
END p_test_clob_mask;
/
我们的期望是在最后一个 DBMS_OUPUT.PUT_LINE
完成后,程序应该完成。
但是经过的时间显示 20 分钟以上,而根据最后的 DBMS_OUPUT.PUT_LINE
声明,所用时间仅为 3 分钟
下面是输出:
OUTSIDEBLOCKSTARTTIME
-------------------------------------------------------
Outside block Start time: 27-DEC-2018 15:33:45
Elapsed: 00:00:00.118
Overall Start: 27-DEC-18 15:33:45
Overall End: 27-DEC-18 15:37:37 - Time taken in Mins: 3.86 - Counters: 138913
PL/SQL procedure successfully completed.
Elapsed: 00:20:19.313
OUTSIDEBLOCKENDTIME
-----------------------------------------------------
Outside block End time: 27-DEC-2018 15:54:04
Elapsed: 00:00:00.095
根据评论,修复是添加行
DBMS_LOB.freetemporary(l_masked_clob);
在循环结束时。这确保使用的临时 CLOB
s 在使用时被清理。编写的代码导致 Oracle 为循环的每次迭代创建一个新的临时 CLOB
,并且很可能清除所有这些临时 CLOB
(可能有数百甚至数千个)在块的末尾导致延迟。
另一种可能的性能提升可以通过在循环外创建一个临时 LOB
并在每次迭代结束时将其清除(例如使用 DBMS_LOB.erase
或 DBMS_LOB.trim
)来获得循环,并在最后释放它。