增强的 COMMIT
当提交会话时,将刷新重做日志缓冲区,将其内容存储到磁盘上的联机重做日志中。此过程确保在对数据库进行恢复操作时,可以根据需要利用重做日志中回放事务处理。
但是有时您可能想对一部分受保证的恢复能力进行调整,以获得更好的性能。利用 Oracle 数据库 10g 第 2 版,您现在可以控制如何将重做流写入到联机日志文件。您可以在执行提交语句时控制这种行为,或者只需更改数据库的默认行为即可。
让我们来看提交语句是工作过程。在事务处理后,当执行 COMMIT 时,可以附带一个子句:
COMMIT WRITE <option> |
其中 <option> 是影响重做流的部分。选项 WAIT 是默认行为。例如,您可以执行:
COMMIT WRITE WAIT; |
此命令与 COMMIT 本身的效果相同。在重做流写入到联机重做日志文件之前,提交命令不会将控制权交还给用户。如果您不希望等待,则可以执行:
COMMIT WRITE NOWAIT; |
这样,控制权立即返还给会话,甚至是在将重做流写入联机重做日志之前。
当执行提交命令时,日志写入器进程将重做流写入到联机重做日志。如果您正在进行一系列事务处理(如在批处理环境中),则可能不希望如此频繁地进行提交。当然,最好的操作过程是更改应用程序以减少提交数量;但这可能说起来容易做起来难。在这种情况下,您只需执行以下的提交语句:
COMMIT WRITE BATCH; |
此命令将以批量方式将重做流写入到日志文件中,而不是每次提交都执行写操作。在频繁提交的环境中,您可以使用这种技术来减少日志缓冲区刷新。如果您希望立即写入日志缓冲区,则可以执行:
COMMIT WRITE IMMEDIATE; |
如果您数据库默认使用某种特定的提交行为,则可以执行以下语句。
ALTER SYSTEM SET COMMIT_WRITE = NOWAIT; |
此命令将使数据库默认使用这一行为。您还可以使其成为会话级默认行为:
ALTER SESSION SET COMMIT_WORK = NOWAIT; |
对于任何参数,如果完成设置后,则该参数在系统级执行。如果有会话级的设置,则会话级设置优先,而最后如果 COMMIT 语句后面有子句,则该子句优先。
此选项不可用于分布式事务处理。
记录错误并继续运行:错误事件记录子句
假设您正试图将表 ACCOUNTS_NY 的记录插入到表 ACCOUNTS 中。表 ACCOUNTS 在 ACC_NO 列上有一个主键。可能 ACCOUNTS_NY 中的某些行与该主键冲突。尝试使用一个常规的插入语句:
SQL> insert into accounts 2 select * from accounts_ny; insert into accounts * ERROR at line 1: ORA-00001:unique constraint (ARUP.PK_ACCOUNTS) violated |
表 ACCOUNTS_NY 中的记录均未被装载。现在,将错误事件记录项打开,尝试同样的操作。首先,您需要创建一个表来保存由 DML 语句所拒绝的记录。调用该表 ERR_ACCOUNTS。
exec dbms_errlog.CREATE_ERROR_LOG ('ACCOUNTS','ERR_ACCOUNTS') |
接下来,执行前面的语句,并加入事件记录子句。
SQL> insert into accounts 2 select * from accounts_ny 3 log errors into err_accounts 4 reject limit 200 5 / 6 rows created. |
注意,表 ACCOUNTS_NY 包含 10 行,但只有六行被插入;其他四行由于某种错误而被拒绝。要找出错误是什么,可查询 ERR_ACCOUNTS 表。
SQL> select ORA_ERR_NUMBER$, ORA_ERR_MESG$, ACC_NO 2 from err_accounts; ORA_ERR_NUMBER$ ORA_ERR_MESG$ ACC_NO --------------- -------------------------------------------------- ------ 1 ORA-00001:unique constraint (ARUP.PK_ACCOUNTS) vi 9997 olated 1 ORA-00001:unique constraint (ARUP.PK_ACCOUNTS)vi 9998 olated 1 ORA-00001:unique constraint (ARUP.PK_ACCOUNTS) vi 9999 olated 1 ORA-00001:unique constraint (ARUP.PK_ACCOUNTS) vi 10000 olated |
请注意列 ORA_ERR_NUMBER$,它显示在 DML 语句执行期间所遇到的 Oracle 错误号,还有 ORA_ERR_MESG$,它显示错误消息。在本例中,您可以看到四条记录被丢弃是因为它们与主键约束 PK_ACCOUNTS 相冲突。该表还捕获表 ACCOUNTS 的所有列,包括列 ACC_NO。查看被拒绝的记录,注意这些帐号已经在表中存在,因此这些记录由于 ORA-00001 错误而被拒绝。如果没有错误事件记录子句,则整个语句将会出错,不会拒绝任务记录。通过这个子句,只有无效的记录被拒绝;其他所有记录均得以接受。
从源头保护代码:WRAP 程序包
PL/SQL 程序单元经常包含关于公司流程和商业秘密的非常敏感和机密的信息,这使得它们与表相类似,成为受保护的实体组。为防止未经授权而查看源代码的情况,我们要经常使用 wrap 命令行实用程序,这将使程序变得很混乱。
只有在创建 PL/SQL 脚本后才能调用 wrap;该实用程序将输入的明文打包为一个文件。但是,在某些情况下,您可能希望在 PL/SQL 代码中动态生成包装。在这种情况下,因为还不存在源文件,不能调用 wrap 实用程序。
由于 Oracle 数据库 10g 第 2 版提供了一个供应程序包,您可以使用它创建代码,并进行打包。该程序包补充(而不是替代)了 wrap 实用程序。而后者仍然适合于希望使用命令行来快速打包大量源文件的情况。
例如,假设您希望以打包形式创建简单的过程 p1。
create or replace procedure p1 as begin null; end; |
在 PL/SQL 单元中,您可以使用以下命令以打包方式动态地创建这一过程:
begin dbms_ddl.create_wrapped ('create or replace procedure p1 as begin null; end;') end; / |
现在您希望确认打包过程。您可以从字典中选择源文本。
SQL> select text from user_source where name = 'P1'; Text ----------------------------------------------------------------- procedure p1 wrapped a000000 369 abcd abcd |
……等等……
第一行 procedure p1 wrapped 是确认以打包方式创建过程。如果您利用 DBMS_METADATA.GET_DDL() 函数来获取该过程的 DDL,则仍然会看到源代码已被打包。
有时您可能会有略微不同的需求;例如,您可能要生成 PL/SQL 代码,但不想创建过程。在这种情况下,您可以将其保存在一个文件或表中,以便以后执行。但是因为以上方法创建了过程,所以该方法在这里行不通。所以您需要在程序包中调用另一个函数:
SQL> select dbms_ddl.wrap 2 ('create or replace procedure p1 as begin null; end;') 3 from dual 4 / DBMS_DDL.WRAP('CREATEORREPLACEPROCEDUREP1ASBEGINNULL;END;') ---------------------------------------------------------------------- create or replace procedure p1 wrapped a000000 369 abcd abcd ... and so on ... |
WRAP 函数的输出是一个可传递的参数,它代表着 PL/SQL 代码的打包输出结果。该参数可以保存在纯文件文件或表中,可以在以后执行。如果您生成的代码要在其他地方部署,并且必须要保证代码的安全性,则这种方法很有用。
如果您可以将所存储代码的全部文本作为一个 varchar2 数据类型(大小限制为 32K)来传递,则这一方法可以正常工作。如果 PL/SQL 代码超过 32K,则您必须使用一种略微不同的方法:接受一个集合变量作为输入。
在这里您可以使用一个供应的数据类型:程序包 DBMS_SQL 中的 varchar2。这是一个集合数据类型 (TABLE OF VARCHAR2),表的每个单元都接收多达 32K 的文本;可随意增加该表所含的单元数,以满足您的需要。例如,假设您必须包装一个名为 myproc 的非常长的过程,其定义如下:
create or replace procedure myproc as l_key VARCHAR2(200); begin l_key := 'ARUPNANDA'; end; |
当然,这根本不是一个非常长的过程;但是为了示范起见,假设它很长。为了将其创建为打包形式,您要执行以下的 PL/SQL 块:
1 declare 2 l_input_code dbms_sql.varchar2s; 3 begin 4 l_input_code (1) := 'Array to hold the MYPROC'; 5 l_input_code (2) := 'create or replace procedure myproc as '; 6 l_input_code (3) := ' l_key VARCHAR2(200);'; 7 l_input_code (4) := 'begin '; 8 l_input_code (5) := ' l_key := ''ARUPNANDA'';'; 9 l_input_code (6) := 'end;'; 10 l_input_code (7) := 'the end'; 11 sys.dbms_ddl.create_wrapped ( 12 ddl => l_input_code, 13 lb => 2, 14 ub => 6 15 ); 16* end; |
在这里我们定义了一个变量 l_input_code 来保存输入的明文代码。在第 4 行到第 10 行中,我们用要打包的代码来填充这些行。在本示例中,同样为了简单起见,我使用了非常短的行。实际上,您可能要使用非常长的行,其大小多达 32KB。同样,我在数组中只使用了 7 个单元;实际上您可能要使用若干单元来填充全部代码。 第 11 到第 15 行表明我如何调用该过程,以便将该过程创建为打包形式。在第 12 行中,我将集合作为一个参数 DDL 来传递。但是,在这里暂停一下 — 我已经分配了一个注释作为数组的第一个单元,可能用于文档。但它不是有效的语法。同样,我将另一个注释分配给数组的最后一个单元 (7),它也不是用于创建过程的有效语法。为了使包装操作仅仅处理有效的行,我在第 13 和第 14 行中指定了存储我们代码的集合的最低 (2) 和最高 (6) 的单元。参数 LB 表示数组的下界,在本示例中是 2,而 HB 是上界 (6)。
使用这种方法,现在可以从您的 PL/SQL 代码中以打包方式创建任意大小的过程。