PL/SQL 中的条件编译:一次编写,多次执行
你们中很多人曾经使用过 C 语言,它支持编译器指令的概念。在 C 程序中,根据相关编译器的版本,特定变量的值有可能不同。
在 Oracle 数据库 10g 第 2 版中,PL/SQL 有一个类似的特性:现在可以提供预处理器指令,它们在编译期间而不是在运行时进行求值。例如,让我们创建一个非常简单的返回字符串的函数。
1 create or replace function myfunc 2 return varchar2 3 as 4 begin 5 $if $$ppval $then 6 return 'PPVAL was TRUE'; 7 $else 8 return 'PPVAL was FALSE'; 9 $end 10* end; |
注意第 5 行,您已经使用预处理器指令为变量 ppval 求值。因为 ppval 是一个预处理器变量,而不是常规的 PL/SQL 变量,所以使用 $$ 标志来指定它。同样,为了编译器能分辨自己只需在编译期间处理这些行,你要用特殊的 $ 标志来指定求值项,例如用 $if 代替 if。现在,利用变量 ppval 的不同值来编译这个函数。
SQL> alter session set plsql_ccflags = 'PPVAL:TRUE'; Session altered. |
现在编译该函数并执行它。
SQL> alter function myfunc compile; Function altered. SQL> select myfunc from dual; MYFUNC ------------------------------------- PPVAL was TRUE |
在编译期间 ppval 的值被设为 false。现在更改该变量的值并重新执行该函数。
SQL> alter session set plsql_ccflags = 'PPVAL:FALSE'; Session altered. SQL> select myfunc from dual; MYFUNC --------------------------------------------------------- PPVAL was TRUE |
虽然这里 ppval 的值在会话中是 FALSE,但函数没有采用它;而是采用了在编译期间所设置的值。现在,重新编译该函数并执行它。
SQL> alter function myfunc compile;
Function altered. SQL> select myfunc from dual; MYFUNC --------------------------------------------------- PPVAL was FALSE |
在编译期间,ppval 的值是 FALSE,而这就是所返回的值。
那么您如何利用这个特性呢?有几种可能性 — 例如,您可以将它用作一个调试标志来显示更多的消息,或者可以编写一个程序,这个程序在各个平台上进行不同的编译。因为求值是在编译期间而不是在运行时间内完成的,运行时效率得到显著增强。
当您拥有相同的预处理器标志(在所有将要编译的函数中引用该标志)时,以上示例运行正常。但是如果您的每段代码具有不同的标志,情况会怎样?例如,函数 calculate_interest 可能将标志 ACTIVE_STATUS_ONLY 设为 TRUE,而函数 apply_interest 可能将标志 FOREIGN_ACCOUNTS 设为 FALSE。为了利用相应的标志来编译这些函数,您可以执行:
alter function calculate_interest compile plsql_ccflags = 'ACTIVE_STATUS_ONLY:TRUE' reuse settings; alter function apply_interest compile plsql_ccflags = FOREIGN_ACCOUNTS:TRUE' reuse settings; |
注意,这些设置都不是会话级的。子句 reuse settings 确保在以后重新编译函数时使用相同的编译器指令。
让我们来看这个新特性的另一个变体。除了条件变量的定义之外,您还可以在条件编译中检查程序包的静态常量。例如,假设您希望基于一个布尔型打包常数来控制 PL/SQL 过程的调试输出。首先创建程序包:
create or replace package debug_pkg is debug_flag constant boolean := FALSE; end; |
debug_flag 是在代码中确定条件逻辑的常数。现在您可以将代码嵌入程序包,如下所示:
create or replace procedure myproc as begin $if debug_pkg.debug_flag $then dbms_output.put_line ('Debug=T'); $else dbms_output.put_line ('Debug=F'); $end end; |
注意,打包的常量被直接引用,没有任何 $ 符号。在本案例中,不需要设置任何会话级或系统级的条件编译参数。在编译函数时,您也不需要传递任何额外的子句。要了解具体的工作过程,可执行:
SQL> exec myproc Debug=F |
因为现在 debug_pkg.debug_flag 的值是 FALSE,所以执行该过程返回了预期的“F”。现在,更改常数值:
create or replace package debug_pkg is debug_flag constant boolean := TRUE; end; |
然后再次执行该过程:
SQL> exec myproc Debug=T |
该过程获该常量的值,即预期的“T”。注意这里有一个非常重要的区别 — 您不需要重新编译过程;将自动获取对常量的更改!
无限制的 DBMS 输出
还记得类似以下各行的令人讨厌的错误吗?
ERROR at line 1: ORA-20000:ORU-10027:buffer overflow, limit of 1000000 bytes ORA-06512:at "SYS.DBMS_OUTPUT", line 32 ORA-06512:at "SYS.DBMS_OUTPUT", line 97 ORA-06512:at "SYS.DBMS_OUTPUT", line 112 ORA-06512:at line 2 |
这是由于供应程序包 dbms_output 过去能够处理的最大字符数量是 1 百万字节。在 Oracle 数据库 10g 第 2 版中,该限制已经解除:现在最大输出数量是不封顶的。您只需通过执行以下命令,就可以将其设为“unlimited”
set serveroutput on
以上语句的输出结果如下:
SQL> show serveroutput serveroutput ON size 2000 format WORD_WRAPPED |
注意输出的最大默认值过去是 2000 。在 Oracle 数据库 10g 第 2 版中,该命令显示以下结果:
SQL> show serveroutput serveroutput ON SIZE UNLIMITED FORMAT WORD_WRAPPED |
默认值是 UNLIMITED。
老版本的另一个不便之处是 dbms_output 所显示的行的最大长度。以下是行的长度超过 255 字节时的一个典型错误消息。
ERROR at line 1: ORA-20000:ORU-10028:line length overflow, limit of 255 chars per line ORA-06512:at "SYS.DBMS_OUTPUT", line 35 ORA-06512:at "SYS.DBMS_OUTPUT", line 115 ORA-06512:at line 2 |
在 Oracle 数据库 10g 第 2 版中,行可以具有任意长度。