我在匿名块中有很多代码,我担心PL / SQL优化器不会优化该代码。我应该将我的代码从匿名块移动到包中以确保优化器为我做了所有繁重的工作吗?
答案 0 :(得分:6)
标题问题的答案是:是的。优化器优化匿名块以及存储的程序单元。请参阅答案末尾的脚本以验证此声明。
第二个问题的答案是:是的,您应该将代码从匿名块移到包中。
第二个是的原因与性能无关。它与管理大量代码的挑战有关。管理块(脚本文件)中的代码比处理包中的代码要困难得多。此外,当您将代码存储在指定的程序单元中时,您可以更充分地利用Oracle数据库为代码执行的所有操作,包括依赖关系管理,程序失效和自动重新编译,使用PL / Scope进行代码分析。
现在看看优化器确实在匿名块上发挥了作用,请考虑在12.2中执行的以下语句序列。
首先,我将设置计时并将优化级别设置为最大可能级别(级别2是默认值并执行大部分优化;级别3添加子程序内联,并由PL / SQL开发团队推荐)。
SET TIMING ON
ALTER SESSION SET plsql_optimize_level = 3
/
在第一个块中,我使用游标FOR循环遍历一大堆行。优化器应自动将其编译为等效的BULK COLLECT语句(默认情况下一次提取100行)。
SQL> DECLARE
2 n INT;
3 BEGIN
4 FOR rec IN (SELECT * FROM all_objects)
5 LOOP
6 n := n + 1;
7 END LOOP;
8
9 DBMS_OUTPUT.put_line (n);
10 END;
11 /
Elapsed: 00:00:01.943
所以不到2秒。它是否优化了?让我们通过比较来检查。
在第二个块中,我不再使用游标FOR循环。相反,我显式声明一个游标,然后一次迭代一行。通过采用这种方法,优化器无法再将此代码安全地转换为批量提取,因此速度要慢得多。
SQL> DECLARE
2 n INT;
3
4 CURSOR obj_cur
5 IS
6 SELECT * FROM all_objects;
7
8 r obj_cur%ROWTYPE;
9 BEGIN
10 OPEN obj_cur;
11
12 LOOP
13 FETCH obj_cur INTO r;
14 EXIT WHEN obj_cur%NOTFOUND;
15 n := n + 1;
16 END LOOP;
17
18 CLOSE obj_cur;
19
20 DBMS_OUTPUT.put_line (n);
21 END;
22 /
Elapsed: 00:00:04.648
比第一个块慢很多。结论:优化了匿名块。只是为了推动这一点,让我们将第一块的性能与存储过程的性能进行比较:
SQL> CREATE OR REPLACE PROCEDURE count_objects
2 IS
3 n INT;
4 BEGIN
5 FOR rec IN (SELECT * FROM all_objects)
6 LOOP
7 n := n + 1;
8 END LOOP;
9
10 DBMS_OUTPUT.put_line (n);
11 END;
12 /
SQL> BEGIN
2 count_objects;
3 END;
4 /
Elapsed: 00:00:01.875
大致相同。所以你有它:匿名块就像存储的程序单元一样被优化。享受!