【转】使用 PL/SQL 条件编译

来源:互联网 发布:linux切换登录用户命令 编辑:程序博客网 时间:2024/06/05 17:52

预处理器指令

指令由指令控制标记“$”和普通的 PL/SQL 文本组成。条件编译使用三个指令:选择、查询和错误。特殊的触发器字符“$”代表条件编译指令。选择指令是条件编译机制的重要组成部分,而查询和错误指令支持有用的附加功能。

 

选择指令

选择指令对条件编译表达式进行评估,并根据评估的结果选择要包含在该编译中的代码。完全忽略未选中的代码。这不会干扰到现有程序,因为这些程序并未使用条件编译。条件选择指令以 $if 开始并使用常规语法:

$if $then $elsif $then  $else $end

其中, 表示静态布尔表达式。静态布尔表达式是一个或多个程序包常量或一个或多个查询指令的任意组合。以下是选择指令将程序包常量用作静态布尔表达式的示例:

$if Trace_Pkg.Trace > 2 $then ?$end

其中,Trace_pkg 是程序包的名称,Trace 是声明为 PLS_INTEGER 的常量。请注意,如果 PL/SQL 编译单元 U内的选择指令中使用了在程序包 Trace_pkg 内声明的常量,那么,U 将在 Trace_pkg 上具有一个依赖项,就好像 Trace_pkg 已经在常规 PL/SQL 代码中进行了引用。

 

查询指令

查询指令允许访问编译环境,以便将选择基于当前环境。查询指令的形式为 $$ 开头,例如:

$if $$debug_level > 3 $then … $end

标识符 debug_level 是使用 plsql_ccflags 初始化参数(Oracle 数据库 10g 第 2 版中的新增内容)定义的,因此:

...plsql_ccflags = 'debug_level:4, ...'

 

错误指令

错误指令的形式如下所示

$error $end。

它可以使编译器报告编译错误,包括 VARCHAR2 表达式中提供的消息。

 

预定义编译器参数

Oracle 数据库 10g 第 2 版提供了以下可以在条件查询指令中使用的 PL/SQL 编译器参数:

使用 <wbr>PL/SQL <wbr>条件编译

PLSQL_CCFLAGS

使用 <wbr>PL/SQL <wbr>条件编译

PLSQL_DEBUG

使用 <wbr>PL/SQL <wbr>条件编译

PLSQL_OPTIMIZE_LEVEL

使用 <wbr>PL/SQL <wbr>条件编译

PLSQL_CODE_TYPE

使用 <wbr>PL/SQL <wbr>条件编译

PLSQL_WARNINGS

使用 <wbr>PL/SQL <wbr>条件编译

NLS_LENGTH_SEMANTICS

编译时,PL/SQL 编译器参数的值存储在编译单元中,并可以使用 all_plsql_object_settings 系列视图进行查看。此外,还提供了预定义的查询指令 $$PLSQL_UNIT$$PLSQL_LINE。有关条件编译指令语法的详细信息,请参阅 PL/SQL 用户指南和参考 手册。

 

使用指令

使用 <wbr>PL/SQL <wbr>条件编译

示例 1:使用程序包常量进行跟踪和调试

使用 <wbr>PL/SQL <wbr>条件编译

示例 2:使用编译器警告和 PLSQL_CCFLAGS

使用 <wbr>PL/SQL <wbr>条件编译

Example 3:Using DBMS_PREPROCESSOR Procedures to Print or Retrieve Source Text 

使用 <wbr>PL/SQL <wbr>条件编译

示例 4:使用条件编译分支代码确定最佳性能版本

使用 <wbr>PL/SQL <wbr>条件编译

示例 5:将条件编译与不同的 Oracle 数据库版本结合使用

 

示例 1:使用程序包常量进行跟踪和调试

在条件编译指令中使用程序包常量为通过单一机制控制一个或多个 PL/SQL 编译单元提供了一种方法。例如,假设应用程序由许多 PL/SQL 编译单元组成。在该应用程序内,已嵌入了执行调试或跟踪的 ed 方法。这些方法可以通过使用程序包常量的条件编译指令启用。因此,可以通过随时重新编译该程序包来更改该常量的值。在重新编译程序包时,所有的相关对象都将自动重新编译,以接受该程序包常量的新值。这可以用于在整个应用程序中启用跟踪和调试功能。在进行跟踪和调试时,利用保护跟踪和调试代码的新常量值重新编译程序包规范。这将使所有相关 PL/SQL 单元无效,以便下一次使用时,将不会选中跟踪和调试代码进行编译。程序包常量的使用是一种控制所有相关 PL/SQL 单元的有效机制,这些单元在选择指令内使用打包常量进行条件处理。以下示例演示了这一用法。

1.

打开一个终端窗口,执行以下命令:

cd /home/oracle/wkdir sqlplus hr/hr

 

2.

创建程序包 STATIC_CONSTANTS 以声明可用在条件编译中的程序包常量。从 SQL*Plus 会话中,执行以下脚本:

@static_constants

static_constants.sql 脚本包含以下内容:

CREATE OR REPLACE PACKAGE static_constants isdebug CONSTANT BOOLEAN := FALSE; trace CONSTANT BOOLEAN := FALSE;END ; /

 

 

3.

创建两个过程:CHECK_DEBUG(用于检查程序包常量调试的值是否是 TRUE)和 CHECK_TRACE(用于检查程序包跟踪的值是否是 TRUE)。从 SQL*Plus 会话中,执行以下脚本:

@debug_trc

debug_trc.sql 脚本包含以下内容:

CREATE OR REPLACE PROCEDURE check_Debug ISBEGIN$IF static_constants.debug $THEN DBMS_OUTPUT.put_line('Debugging ON'); $ELSE DBMS_OUTPUT.put_line('Debugging OFF'); $ENDEND;/CREATE or REPLACE PROCEDURE Check_trace ISBEGIN$IF static_constants.trace $THEN DBMS_OUTPUT.put_line('Tracing ON'); $ELSE DBMS_OUTPUT.put_line('Tracing OFF'); $ENDEND;/

 

 

4.

Set serveroutput on  using the new SIZE UNLIMITED syntax available in Oracle Database 10g Release 2. Now execute both the procedures.您将看到它显示跟踪和调试均被关闭。

注意:可以将每一行单独地复制到 sqlplus 中,但不要一起复制这 3 个语句。

set serveroutput on size unlimited exec check_debug exec check_trace

 

 

5.

现在,将更改程序包常量的值。从 SQL*Plus 会话中,执行以下脚本

@reset_const

reset_const.sql 脚本包含以下内容:

CREATE OR REPLACE PACKAGE static_constants isdebug CONSTANT BOOLEAN := TRUE; trace CONSTANT BOOLEAN := TRUE;END ; /

 

 

6.

现在,再次执行这两个过程。您将看到调试和跟踪自动启用了。在重新编译程序包时,所有相关的对象都变为无效,并在下一次执行时重新编译。因此,程序包常量的新值可即刻适用于所有相关对象。

exec check_debug exec check_trace

 

这可以扩展至任意数量的相关程序。在需要通过单一机制控制大量程序时,使用这个方法最为有效。用例的其他示例包括针对同一应用程序按州更改赋税,或根据许可选项更改软件特性等等。

 

 

示例 2:使用编译器警告和 PLSQL_CCFLAGS

通过设置 ALTER COMPILE 命令只影响正在编译的程序。利用 CREATE or REPLACE 重新编译这些程序,GET_RECORD 的过程,以接受 employee_id 的值,并显示相应的记录。为了保持良好的编程实践,该过程将调用

 

请注意,已删除了所有条件编译指令。

 

2.

创建 SEND_MESSAGE_TO_DBA 和 CHECK_UNIQUE 过程。CHECK_UNIQUE 过程包含两个不同的代码段,一个用于开发中的测试,另一个部署在最终生产环境中。开发代码使用条件编译指令,而生产代码使用传统的 IF-THEN-ELSE 逻辑检查重复的记录。从 SQL*Plus 会话中,执行以下脚本:

@dba_email @chk_unq show errors

dba_email.sql 脚本包含以下内容:

CREATE OR REPLACE PROCEDURE send_message_to_DBA(emp_id number)ISmailhost VARCHAR2(64) := 'mailhost.fictional-domain.com';sender VARCHAR2(64) := 'HR_APP@fictional-domain.com';recipient VARCHAR2(64) := 'DBA@fictional-domain.com';mail_conn utl_smtp.connection;BEGINmail_conn := utl_smtp.open_connection(mailhost, 25);utl_smtp.helo(mail_conn, mailhost);utl_smtp.mail(mail_conn, sender);utl_smtp.rcpt(mail_conn, recipient);-- open_data(), write_data(), and close_data() into a single call to data().utl_smtp.open_data(mail_conn);utl_smtp.write_data(mail_conn, 'A primary key violation has occured for record '||emp_id || 'in the EMPLOYEES table.This is an automatically generated e-mail message.Please do not respond to this, this is an alert.'|| chr(13));utl_smtp.write_data(mail_conn, 'This is line 2.'|| chr(13));utl_smtp.close_data(mail_conn);utl_smtp.quit(mail_conn);EXCEPTIONWHEN OTHERS THEN NULL;END;/

chk_unq.sql 脚本包含以下内容:

Create or replace Procedure check_unique(emp_id NUMBER) isv_num number;force_pk_violation exception;BeginSelect count(*) into v_num from employees where employee_id = emp_id;-- production codeIf v_num > 1 then SEND_MESSAGE_TO_DBA(emp_id);raise force_pk_violation; END IF ;-- Development code $if $$FORCE $then SEND_MESSAGE_TO_DBA(emp_id);raise force_pk_violation;$endEND; /

 


3.

现在,更改会话将 $$force 的值设为 FALSE。从 SQL*Plus 执行以下命令。

ALTER SESSION SET PLSQL_CCFLAGS='force:FALSE';

 

 

4.

现在,再次执行 chk_unq.sql

 

因为变量 $$force 现在的值是 FALSE,所以没有出现警告;

 

5.

创建 GET_RECORD 过程。该过程接受 employee id 并返回员工记录。如果 CHECK_UNIQUE 过程检查出存在重复记录,则会显示一个友好消息。从 SQL*Plus 会话执行以下脚本:

@get_record

get_record.sql 脚本包含以下内容:

Create or replace procedure get_record(emp_id IN NUMBER)asemp_record employees%rowtype;Begincheck_unique(emp_id);Select * into emp_record from employees where employee_id =emp_id;Dbms_output.put_line ( 'Name:'|| emp_record.first_name||' '||emp_record.last_name||' '||'Hiredate: ' ||emp_record.hire_date||''||'Job:'||' '||emp_record.job_id);ExceptionWhen others then DBMS_OUTPUT.PUT_LINE('We are unable to process your request at this time.Please try again later.We apologize for any inconvenience');End;/

 


6.

现在,执行 GET_RECORD 过程,传入 100 作为参数。

exec GET_RECORD(100)

 

因为没有重复记录,所以该过程成功执行。

 

7.

现在,测试是否在出现重复记录时显示消息。更改会话将 $$force 的值设为 TRUE,并再次执行 GET_RECORD。从 SQL*Plus 会话执行以下命令:

ALTER SESSION SET PLSQL_CCFLAGS='force:TRUE'; exec GET_RECORD(100)

 

由于未重新编译该过程,因此它不会受到影响。


8.

使用 ALTER...COMPILE 语句重新编译 CHECK_UNIQUE 过程,并为 PLSQL_CCFLAGS 参数提供新值。使用 REUSE SETTINGS选项。然后,以值 100 再次执行 GET_RECORD。从 SQL*Plus 会话执行以下命令。

ALTER PROCEDURE CHECK_UNIQUE compile plsql_ccflags = 'force:TRUE' REUSE SETTINGS; exec GET_RECORD(100)

现在,PLSQL_CCFLAGS 值生效,因而程序发出消息。该方法可以用于影响会话内的特定程序,比如后生产调试。然而,每个需要新设置的程序都必须使用 CREATE OR REPLACE 或 ALTER...COMPILE 语句进行重新编译。

 

返回子主题

示例 3:使用 DBMS_PREPROCESSOR 过程打印或检索源文本

DBMS_PREPROCESSOR 子程序会在处理条件编译指令后,打印或检索 PL/SQL 单元的处理后源文本。这个处理后文本是用于编译有效 PL/SQL 单元的实际源。执行以下步骤:

1.

Execute the following command to see the actual source code used to compile a valid PL/SQL program: 

EXEC DBMS_PREPROCESSOR.PRINT_POST_PROCESSED_SOURCE('PROCEDURE', 'HR', 'CHECK_UNIQUE');

 

请注意,已删除了所有条件编译指令。

 

 

示例 4:使用条件编译分支代码确定最佳性能版本

Oracle 数据库 10g 引入了 BINARY_DOUBLE 数据类型,该数据类型可用于算法密集的操作。本例中,将对 BINARY_DOUBLE 数据类型与 NUMBER 数据类型进行比较。需要在两个不同版本中创建同样的代码,一个使用NUMBER,另一个使用 BINARY_DOUBLE。然后,可将两个版本包含在同一过程中,以使用 PLSQL_CCFLAGS 进行测试。执行以下步骤:

1.

启用 ALTER SESSION 以设置标记,从而在两个版本之间进行选择。首先,选择使用 NUMBER 数据类型。从 SQL*Plus 会话中,执行以下命令:

ALTER SESSION SET PLSQL_CCFLAGS = ' numversion:TRUE';

 

2.

您希望知道哪个代码执行速度更快。创建一个 CALC_CIRCLE 过程计算给定半径的圆的周长和面积。从 SQL*Plus 会话中,执行以下脚本:

@num_bdbl

num_bdbl.sql 脚本包含以下内容:

CREATE or REPLACE PROCEDURE Calc_circle( RADIUS $IF $$NUMVERSION $THEN NUMBER $ELSE BINARY_DOUBLE $END)as SUBTYPE my_real IS$IF $$numversion $THEN NUMBER; $ELSE BINARY_DOUBLE;$ENDnum_circ my_real;num_area my_real;BDBL_circ my_real;BDBL_AREA my_real;BEGINnum_CIRC:= (3.14016408289008292431940027343666863227 * 2 * RADIUS); NUM_AREA := (3.14016408289008292431940027343666863227*radius*radius);DBMS_OUTPUT.PUT_LINE('The circumference is:'||num_circ);DBMS_OUTPUT.PUT_LINE('The area is:'||num_area);END ;/

 

 

3.

执行 CALC_CIRCLE 过程,使用数字 1234567890 作为半径

set timing on exec calc_circle(1234567890)

 

 

4.

使用 ALTER COMPILE 命令将 $$numversion 的值更改为 FALSE 来编译 CALC_CIRCLE,并使用相同的参数再次执行 CALC_CIRCLE

ALTER PROCEDURE calc_circle COMPILE plsql_ccflags = 'numversion :false' REUSE SETTINGS;exec calc_circle(1234567890) set timing off

 

您可以看出二者性能间的差异。本例阐明了可以使用条件编译在相同程序内有选择地测试两个版本的代码。

 

 

示例 5:将条件编译与不同的 Oracle 数据库版本结合使用

以下示例演示了 DBMS_DB_VERSION 常量与条件编译的结合使用。其中对 Oracle 数据库版本和发布均进行了检查。这个 CHECK_VERSIONS 过程使用 COMMIT WRITE IMMEDIATE NOWAIT 命令(已在 Oracle 数据库 10g 第 2 版中引入)。如果在 Oracle 数据库早期版本中执行该过程,则无法识别该命令。因此,该过程DBMS_DB_VERSION 程序包检查数据库的版本。 如果返回的版本低于 10.2,那么将使用常规的 COMMIT 提交事务。当在版本为 10.2 或更高版本的数据库中执行该过程时,将使用 COMMIT WRITE IMMEDIATE NOWAIT 命令完成该事务。

1.

现在可以测试 Oracle 数据库版本了。从终端窗口中执行以下脚本:

@check_version

check_version.sql 脚本包含以下内容:

CREATE OR REPLACE PROCEDURE check_version ASBEGIN-- some code which performs transaction processing ...$if DBMS_DB_VERSION.VER_LE_10_2 $then -- traditional commitCOMMIT;DBMS_OUTPUT.PUT_LINE ('The transaction has been successfully committed.');$else -- faster COMMIT supported in 10.2COMMIT WRITE IMMEDIATE NOWAIT;DBMS_OUTPUT.PUT_LINE ('The transaction has been successfully committed.');$endEND;/

 

 

2.

现在,在 SQL*Plus 窗口中执行以下命令来运行 check_version 过程。

exec check_version

 

check_version 的输出说明:无论版本如何,该过程都能成功完成而且对最终用户而言是透明的。因此,没有理由在不同的数据库版本中使用不同的程序单元

0 0