將鼠標(biāo)置于此圖標(biāo)上可以加載和查看本教程的所有屏幕截圖,。(警告:因為此操作會同時加載所有屏幕截圖,所以網(wǎng)速較慢時,,響應(yīng)時間可能會比較長,。)
注意:此外,您還可以在下列步驟中將鼠標(biāo)放在每個單獨的圖標(biāo)上,,從而僅加載和查看與該步驟相關(guān)的屏幕截圖,??梢酝ㄟ^單擊各個屏幕截圖來將其隱藏。
在 Oracle 數(shù)據(jù)庫 10g 中,,PL/SQL 語言提供了幾個增強(qiáng)功能,。在本概述部分中,您將了解以下主題:
返回主題列表
PL/SQL 條件編譯
Oracle 數(shù)據(jù)庫 10g 第 2 版引入了對 PL/SQL 條件編譯的支持,。新語法允許編譯器根據(jù)指定條件編譯部分程序,。應(yīng)用程序開發(fā)人員通過將指令嵌入其 PL/SQL 源程序中來使用條件編譯。在提交 PL/SQL 程序進(jìn)行編譯時,,會運行指令來選擇要編譯的部分程序,。這種特性在以下情況中非常有用:
|
應(yīng)用程序通常包含調(diào)試或跟蹤代碼,該代碼是在開發(fā)周期內(nèi)專門針對測試編寫的,。在生產(chǎn)環(huán)境中運行應(yīng)用程序時并不需要這些代碼,。在早期版本中,所能做的就是針對生產(chǎn)完全刪除這些代碼(這會阻礙后續(xù)的調(diào)試),,或者用運行時 IF 測試將其包圍(導(dǎo)致在程序大小和執(zhí)行速度方面耗費運行時)?,F(xiàn)在,可以在開發(fā)環(huán)境中啟用調(diào)試或跟蹤功能,,并在生產(chǎn)應(yīng)用程序中禁用該功能,。
|
|
應(yīng)用程序通常包含自檢查代碼,以遵從良好的編程實踐,。如果程序設(shè)計所依賴的不變量被破壞,,則這種代碼將引發(fā)異常。理論上,,在生產(chǎn)環(huán)境中應(yīng)該永遠(yuǎn)不會出現(xiàn)這種異常,。然而,這些自測試會導(dǎo)致運行時消耗,,尤其是當(dāng)它們出現(xiàn)在嵌套很深的循環(huán)中時,。但是,在系統(tǒng)測試完成并啟用應(yīng)用程序前,,將其全部刪除將妨礙調(diào)試小組對首次在生產(chǎn)環(huán)境中出現(xiàn)的錯誤做出診斷,。只有少數(shù)開發(fā)團(tuán)隊會保留應(yīng)用程序源代碼的并行副本:一個用于調(diào)試,一個用于生產(chǎn),。PL/SQL 條件編譯支持新的最佳實踐:通過有選擇地編譯自檢查,,保留了診斷功能,且不耗費生產(chǎn)環(huán)境,。
|
|
程序包通常包含私有“幫助”子程序,,當(dāng)然,在開發(fā)期間,需要對這些子程序進(jìn)行測試,。但是,,由于它們是私有的,因此不能用外部測試工具對其進(jìn)行測試,。通過在程序包規(guī)范中的條件指令內(nèi)聲明這些“私有”子程序,,可以在開發(fā)和測試期間從程序包外調(diào)用這些程序,然后在生產(chǎn)環(huán)境中將其隱藏,。
|
|
應(yīng)用程序代碼經(jīng)常調(diào)用服務(wù)子程序,。這種服務(wù)子程序可能返回 PL/SQL 記錄,以描述員工何時通過其唯一標(biāo)識符進(jìn)行了調(diào)用,。最佳實踐指出,,如果假設(shè)唯一關(guān)鍵字找到多個紀(jì)錄,則該服務(wù)例程將引發(fā)定義完善的異常,。(如果數(shù)據(jù)損壞是由整個系統(tǒng)的一個有缺陷的不同組件所導(dǎo)致,,則可能會出現(xiàn)此異常。)最佳實踐進(jìn)一步指出,,調(diào)用服務(wù)子程序的應(yīng)用程序代碼必須在異常出現(xiàn)時表現(xiàn)正常,,例如向用戶顯示一個友好的、相對而言非特定的“出現(xiàn)問題”消息,,并記錄所有相關(guān)信息以向跟蹤文件詳細(xì)說明出現(xiàn)異常的來龍去脈,。當(dāng)然,必須在開發(fā)周期內(nèi)對應(yīng)用程序代碼的此種強(qiáng)健特性進(jìn)行測試,。設(shè)法損壞數(shù)據(jù)使服務(wù)子程序引發(fā)異常通常有一定難度,并且非常耗時,。條件編譯通過有條件地將代碼包含在服務(wù)子程序中隨意引發(fā)給定異常,,使您能夠以低成本獲得同樣的效果。
|
|
在開發(fā)期間,,程序員通常創(chuàng)建兩個版本,、獲得相同結(jié)果的編譯單元。兩種版本必須經(jīng)過測試才能確定哪個最有效,。一般而言,,兩個版本在文本上基本相同,只是在分布于整個源文件上的小關(guān)鍵點處略有不同,。在此種情況下,,與編寫兩個單獨的程序相比,編寫一個包含兩種版本的程序要更容易(不易犯人為錯誤),。條件編譯對此方法提供了豐富的支持,,而這起初需要手工生成兩種版本的源代碼。
|
|
在編寫某些應(yīng)用程序時不僅要考慮在最新的 Oracle 數(shù)據(jù)庫版本上運行,,而且要考慮在早期版本上運行,。通常,,開發(fā)團(tuán)隊只想保留單一的源代碼。這迫使開發(fā)人員避免在最新版本中使用新的強(qiáng)大特性(由新的 PL/SQL 語法提供),,妨礙了用戶將代碼部署 在最新的版本上,。條件編譯可用于在最新版本上和早期版上安裝程序時選擇最優(yōu)的程序源代碼。
|
Oracle 數(shù)據(jù)庫 10g 版本 10.1.0.4 或更高版本中已引入了條件編譯,。
返回主題
了解批量綁定功能增強(qiáng)
Oracle 數(shù)據(jù)庫 10g 擴(kuò)展了 Oracle9i 在批量綁定領(lǐng)域中引入的增強(qiáng)功能,。將 SAVE EXCEPTIONS 語法引進(jìn) Oracle9i,用于在批量插入(刪除或更新)行時捕獲異常,。盡管此特性使數(shù)據(jù)操作語言 (DML) 能夠繼續(xù)將所有異常保存在 SQL%BULK_EXCEPTIONS 集合中,,但操作的性能會受到顯著影響。如果正在處理的集合因所涉及的應(yīng)用程序邏輯而變得比較稀疏,,則這可能是不必要的開銷,。在 Oracle 數(shù)據(jù)庫 10g 中,您可以使用新的 INDICES OF 和 VALUES OF 特性解決這些問題,。
返回主題
使用 JDeveloper 調(diào)試 PL/SQL
從 9.0.3 版開始,,JDeveloper 支持 PL/SQL 調(diào)試。這是通過 Java 調(diào)試線路協(xié)議 (JDWP) 實現(xiàn)的,。在 Oracle9i 第 2 版(以及更高版本)中,,該調(diào)試器使用行業(yè)標(biāo)準(zhǔn),而在早期版本(Oracle8i 和 Oracle9i 第 1 版)中,,則使用 DBMS_DEBUG 程序包,。通過此調(diào)試用戶界面,您可以單步,、步進(jìn)和步過執(zhí)行 PL/SQL 對象,,還能夠在 Watch 和 Inspector 窗口中設(shè)置 PL/SQL 表達(dá)式、設(shè)置條件斷點以及查看集合數(shù)據(jù),。
返回主題
開始本教程之前,,您應(yīng)該:
返回主題列表
本部分將向您介紹如何使用條件編譯,,并給出了幾個用法示例。
返回主題列表
預(yù)處理器指令
指令由指令控制標(biāo)記“$”和普通的 PL/SQL 文本組成,。條件編譯使用三個指令:選擇,、查詢和錯誤。特殊的觸發(fā)器字符“$”代表條件編譯指令,。選擇指令是條件編譯機(jī)制的重要組成部分,,而查詢和錯誤指令支持有用的附加功能。
返回主題
選擇指令
選擇指令對條件編譯表達(dá)式進(jìn)行評估,并根據(jù)評估的結(jié)果選擇要包含在該編譯中的代碼,。完全忽略未選中的代碼,。這不會干擾到現(xiàn)有程序,因為這些程序并未使用條件編譯,。條件選擇指令以 $if 開始并使用常規(guī)語法:
$if <B-expr > $then
<PL/SQL-code-fragment>
$elsif <B-expr > $then
<PL/SQL-code-fragment>
…
$else
<PL/SQL-code-fragment>
$end
其中,,<B-Expr> 表示靜態(tài)布爾表達(dá)式。靜態(tài)布爾表達(dá)式是一個或多個程序包常量或一個或多個查詢指令的任意組合,。以下是選擇指令將程序包常量用作靜態(tài)布爾表達(dá)式的示例:
$if Trace_Pkg.Trace > 2 $then $end
其中,,Trace_pkg 是程序包的名稱,Trace 是聲明為 PLS_INTEGER 的常量,。請注意,,如果 PL/SQL 編譯單元 U 內(nèi)的選擇指令中使用了在程序包 Trace_pkg 內(nèi)聲明的常量,那么,,U 將在 Trace_pkg 上具有一個依賴項,,就好像 Trace_pkg 已經(jīng)在常規(guī) PL/SQL 代碼中進(jìn)行了引用。
返回主題
查詢指令
查詢指令允許訪問編譯環(huán)境,,以便將選擇基于當(dāng)前環(huán)境,。查詢指令的形式為 $$<PL/SQL-identifier>。例如,,要進(jìn)行編譯的單元的 PL/SQL 編譯器參數(shù)的值(例如 PLSQL_OPTIMIZE_LEVEL),。此種查詢可以與條件選擇指令結(jié)合使用來選擇要編譯的程序部分。例如,,可以使用查詢指令查詢 PL/SQL 編譯器參數(shù)的值,。查詢指令以兩個觸發(fā)器字符 $$ 開頭,例如:
$if $$debug_level > 3 $then … $end
標(biāo)識符 debug_level 是使用 plsql_ccflags 初始化參數(shù)(Oracle 數(shù)據(jù)庫 10g 第 2 版中的新增內(nèi)容)定義的,,因此:
...plsql_ccflags = ‘debug_level:4, ...‘
返回主題
錯誤指令
錯誤指令的形式如下所示
$error <VARCHAR2-expression> $end.
它可以使編譯器報告編譯錯誤,,包括 VARCHAR2 表達(dá)式中提供的消息。
返回主題
預(yù)定義編譯器參數(shù)
Oracle 數(shù)據(jù)庫 10g 第 2 版提供了以下可以在條件查詢指令中使用的 PL/SQL 編譯器參數(shù):
|
PLSQL_CCFLAGS |
|
PLSQL_DEBUG |
|
PLSQL_OPTIMIZE_LEVEL |
|
PLSQL_CODE_TYPE |
|
PLSQL_WARNINGS |
|
NLS_LENGTH_SEMANTICS |
編譯時,,PL/SQL 編譯器參數(shù)的值存儲在編譯單元中,并可以使用 all_plsql_object_settings 系列視圖進(jìn)行查看,。此外,,還提供了預(yù)定義的查詢指令 $$PLSQL_UNIT, $$PLSQL_LINE。有關(guān)條件編譯指令語法的詳細(xì)信息,,請參閱 PL/SQL 用戶指南和參考 手冊,。
返回主題
使用指令
返回主題
示例 1:使用程序包常量進(jìn)行跟蹤和調(diào)試
在條件編譯指令中使用程序包常量為通過單一機(jī)制控制一個或多個 PL/SQL 編譯單元提供了一種方法。例如,,假設(shè)應(yīng)用程序由許多 PL/SQL 編譯單元組成,。在該應(yīng)用程序內(nèi),已嵌入了執(zhí)行調(diào)試或跟蹤的 ed 方法。這些方法可以通過使用程序包常量的條件編譯指令啟用,。因此,,可以通過隨時重新編譯該程序包來更改該常量的值。在重新編譯程序包時,,所有的相關(guān)對象都將自動重新編譯,,以接受該程序包常量的新值。這可以用于在整個應(yīng)用程序中啟用跟蹤和調(diào)試功能,。在進(jìn)行跟蹤和調(diào)試時,,利用保護(hù)跟蹤和調(diào)試代碼的新常量值重新編譯程序包規(guī)范。這將使所有相關(guān) PL/SQL 單元無效,,以便下一次使用時,,將不會選中跟蹤和調(diào)試代碼進(jìn)行編譯。程序包常量的使用是一種控制所有相關(guān) PL/SQL 單元的有效機(jī)制,,這些單元在選擇指令內(nèi)使用打包常量進(jìn)行條件處理,。以下示例演示了這一用法。
1. |
打開一個終端窗口,,執(zhí)行以下命令:
cd\wkdir
sqlplus hr/hr
|
2. |
創(chuàng)建程序包 STATIC_CONSTANTS 以聲明可用于條件編譯的程序包常量,。從 SQL*Plus 會話中,執(zhí)行以下腳本:
@static_constants
static_constants.sql 腳本包含以下內(nèi)容:
CREATE OR REPLACE PACKAGE static_constants is debug CONSTANT BOOLEAN := FALSE; trace CONSTANT BOOLEAN := FALSE; END ; /
|
3. |
創(chuàng)建兩個過程:CHECK_DEBUG(用于檢查程序包常量調(diào)試的值是否是 TRUE)和 CHECK_TRACE(用于檢查程序包跟蹤的值是否是 TRUE),。從 SQL*Plus 會話中,,執(zhí)行以下腳本:
@debug_trc
debug_trc.sql 腳本包含以下內(nèi)容:
CREATE OR REPLACE PROCEDURE check_Debug IS BEGIN $IF static_constants.debug $THEN DBMS_OUTPUT.put_line(‘Debugging ON‘); $ELSE DBMS_OUTPUT.put_line(‘Debugging OFF‘); $END END; / CREATE or REPLACE PROCEDURE Check_trace IS BEGIN $IF static_constants.trace $THEN DBMS_OUTPUT.put_line(‘Tracing ON‘); $ELSE DBMS_OUTPUT.put_line(‘Tracing OFF‘); $END END; /
|
4. |
Set serveroutput on 使用的是 Oracle 數(shù)據(jù)庫 10g 第 2 版中提供的新 SIZE UNLIMITED 語法。現(xiàn)在執(zhí)行這兩個過程,。您將看到它顯示跟蹤和調(diào)試均被關(guān)閉,。
注意:可以將每一行單獨地復(fù)制到 sqlplus 中,但不要一起復(fù)制這 3 個語句,。
set serveroutput on size unlimited
exec check_debug
exec check_trace
|
5. |
現(xiàn)在,,將更改程序包常量的值。從 SQL*Plus 會話中,,執(zhí)行以下腳本:
@reset_const
reset_const.sql 腳本包含以下內(nèi)容:
CREATE OR REPLACE PACKAGE static_constants is debug CONSTANT BOOLEAN := TRUE; trace CONSTANT BOOLEAN := TRUE; END ; /
|
6. |
現(xiàn)在,,再次執(zhí)行這兩個過程。您將看到調(diào)試和跟蹤自動啟用了,。在重新編譯程序包時,,所有相關(guān)的對象都變?yōu)闊o效,并在下一次執(zhí)行時重新編譯,。因此,,程序包常量的新值可即刻適用于所有相關(guān)對象。
exec check_debug
exec check_trace
這可以擴(kuò)展至任意數(shù)量的相關(guān)程序,。在需要通過單一機(jī)制控制大量程序時,,使用這個方法最為有效,。用例的其他示例包括針對同一應(yīng)用程序按州更改賦稅,或根據(jù)許可選項更改軟件特性等等,。
|
返回子主題
示例 2:使用編譯器警告和 PLSQL_CCFLAGS
通過設(shè)置 PLSQL_CCFLAGS 的值,,可以使用條件參數(shù) PLSQL_CCFLAGS 提供對指定程序的細(xì)粒度訪問。為此,,可以使用 ALTER SESSION 命令或 ALTER...COMPILE 命令,。ALTER COMPILE 命令只影響正在編譯的程序。利用 ALTER SESSION 語句之后的 CREATE or REPLACE 重新編譯這些程序,,ALTER SESSION 命令可用于影響多個程序,。
在以下示例中,要創(chuàng)建一個稱為 GET_RECORD 的過程,,以接受 employee_id 的值,,并顯示相應(yīng)的記錄。為了保持良好的編程實踐,,該過程將調(diào)用 CHECK_UNIQUE 過程來驗證每個員工記錄的唯一性,。只有極少數(shù)的情況下存在重復(fù)的記錄,程序會發(fā)出一個用戶友好的消息,,并使用名為 SEND_MESSAGE_TO_DBA 的過程向 DBA 發(fā)送預(yù)警,。然而,因為測試數(shù)據(jù)庫中的數(shù)據(jù)并不包含任何重復(fù)項,,所以可以在查詢指令中使用帶有靜態(tài)布爾表達(dá)式的 PLSL_CCFLAGS 模擬重復(fù)項,。
1.
|
更改會話以顯示所有的編譯器警告。從 SQL*Plus 會話執(zhí)行以下語句:
ALTER SESSION SET plsql_warnings = ‘enable:all‘;
請注意,,已刪除了所有條件編譯指令,。
|
2.
|
創(chuàng)建 SEND_MESSAGE_TO_DBA 和 CHECK_UNIQUE 過程。CHECK_UNIQUE 過程包含兩個不同的代碼段,,一個用于開發(fā)中的測試,,另一個部署在最終生產(chǎn)環(huán)境中。開發(fā)代碼使用條件編譯指令,,而生產(chǎn)代碼使用傳統(tǒng)的 IF-THEN-ELSE 邏輯檢查重復(fù)的記錄,。從 SQL*Plus 會話中,執(zhí)行以下腳本:
@dba_email
@chk_unq
show errors
dba_email.sql 腳本包含以下內(nèi)容:
CREATE OR REPLACE PROCEDURE send_message_to_DBA(emp_id number) IS mailhost VARCHAR2(64) := ‘mailhost.fictional-domain.com‘; sender VARCHAR2(64) := ‘[email protected]‘; recipient VARCHAR2(64) := ‘[email protected]‘; mail_conn utl_smtp.connection; BEGIN mail_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 occurred 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); EXCEPTION WHEN OTHERS THEN NULL; END; /
chk_unq.sql 腳本包含以下內(nèi)容:
Create or replace Procedure check_unique(emp_id NUMBER) is v_num number; force_pk_violation exception; Begin Select count(*) into v_num from employees where employee_id = emp_id; -- production code If 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; $end END; /
|
3. |
現(xiàn)在,,更改會話,,將 $$force 的值設(shè)為 FALSE。從 SQL*Plus 執(zhí)行以下命令,。
ALTER SESSION SET PLSQL_CCFLAGS=‘force:FALSE‘;
|
4. |
現(xiàn)在,,再次執(zhí)行 chk_unq.sql,。
因為變量 $$force 現(xiàn)在的值是 FALSE,,所以沒有出現(xiàn)警告,;
|
5. |
創(chuàng)建 GET_RECORD 過程。該過程接受 employee id 并返回員工記錄,。如果 CHECK_UNIQUE 過程檢查出存在重復(fù)記錄,,則會顯示一個友好消息。從 SQL*Plus 會話執(zhí)行以下腳本:
@get_record
get_record.sql 腳本包含以下內(nèi)容:
Create or replace procedure get_record(emp_id IN NUMBER) as emp_record employees%rowtype; Begin check_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); Exception When 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. |
現(xiàn)在,,執(zhí)行 GET_RECORD 過程,,傳入 100 作為參數(shù)。
exec GET_RECORD(100)
因為沒有重復(fù)記錄,,所以該過程成功執(zhí)行,。
|
7. |
現(xiàn)在,測試是否在出現(xiàn)重復(fù)記錄時顯示消息,。更改會話,,并將 $$force 的值設(shè)為 TRUE,并再次執(zhí)行 GET_RECORD,。從 SQL*Plus 會話執(zhí)行以下命令:
ALTER SESSION SET PLSQL_CCFLAGS=‘force:TRUE‘;
exec GET_RECORD(100)
由于未重新編譯該過程,,因此它不會受到影響。
|
8. |
使用 ALTER...COMPILE 語句重新編譯 CHECK_UNIQUE 過程,,并為 PLSQL_CCFLAGS 參數(shù)提供新值,。使用 REUSE SETTINGS 選項。然后,,以值 100 再次執(zhí)行 GET_RECORD,。從 SQL*Plus 會話中,執(zhí)行以下命令,。
ALTER PROCEDURE CHECK_UNIQUE compile
plsql_ccflags = ‘force:TRUE‘ REUSE SETTINGS;
exec GET_RECORD(100)
現(xiàn)在,,PLSQL_CCFLAGS 值生效,因而程序發(fā)出消息,。該方法可以用于影響會話內(nèi)的特定程序,,比如后生產(chǎn)調(diào)試。然而,,每個需要新設(shè)置的程序都必須使用 CREATE OR REPLACE 或 ALTER...COMPILE 語句進(jìn)行重新編譯,。
|
返回子主題
示例 3:使用 DBMS_PREPROCESSOR 過程打印或檢索源文本
DBMS_PREPROCESSOR 子程序會在處理條件編譯指令后,打印或檢索 PL/SQL 單元的處理后源文本,。這個處理后文本是用于編譯有效 PL/SQL 單元的實際源,。執(zhí)行以下步驟:
1.
|
執(zhí)行以下命令,查看用于編譯有效 PL/SQL 程序的實際源代碼:
EXEC DBMS_PREPROCESSOR.PRINT_POST_PROCESSED_SOURCE(‘PROCEDURE‘, ‘HR‘, ‘CHECK_UNIQUE‘);
請注意,,已刪除了所有條件編譯指令,。
|
返回子主題
示例 4:使用條件編譯分支代碼確定最佳性能版本
Oracle 數(shù)據(jù)庫 10g 引入了 BINARY_DOUBLE 數(shù)據(jù)類型,該數(shù)據(jù)類型可用于算法密集的操作,。本例中,,將對 BINARY_DOUBLE 數(shù)據(jù)類型與 NUMBER 數(shù)據(jù)類型進(jìn)行比較,。需要在兩個不同版本中創(chuàng)建同樣的代碼,一個使用 NUMBER,,另一個使用 BINARY_DOUBLE,。然后,可將兩個版本包含在同一過程中,,以使用 PLSQL_CCFLAGS 進(jìn)行測試,。執(zhí)行以下步驟:
1.
|
啟用 ALTER SESSION 以設(shè)置標(biāo)記,從而在兩個版本之間進(jìn)行選擇,。首先,,選擇使用 NUMBER 數(shù)據(jù)類型。從 SQL*Plus 會話中,,執(zhí)行以下命令:
ALTER SESSION SET PLSQL_CCFLAGS = ‘ numversion:TRUE‘;
|
2.
|
您希望知道哪個代碼執(zhí)行速度更快,。創(chuàng)建一個 CALC_CIRCLE 過程計算給定半徑的圓的周長和面積。從 SQL*Plus 會話中,,執(zhí)行以下腳本:
@num_bdbl
num_bdbl.sql 腳本包含以下內(nèi)容:
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; $END num_circ my_real; num_area my_real; BDBL_circ my_real; BDBL_AREA my_real; BEGIN num_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.
|
執(zhí)行 CALC_CIRCLE 過程,,使用數(shù)字 1234567890 作為半徑。
set timing on
exec calc_circle(1234567890)
|
4.
|
使用 ALTER COMPILE 命令將 $$numversion 的值更改為 FALSE 來編譯 CALC_CIRCLE,,并使用相同的參數(shù)再次執(zhí)行 CALC_CIRCLE,。
ALTER PROCEDURE calc_circle COMPILE plsql_ccflags = ‘numversion :false‘ REUSE SETTINGS;
exec calc_circle(1234567890)
set timing off
您可以看出二者性能間的差異。本例闡明了可以使用條件編譯在相同程序內(nèi)有選擇地測試兩個版本的代碼,。
|
返回子主題
示例 5:將條件編譯與不同版本的 Oracle 數(shù)據(jù)庫結(jié)合使用
以下示例演示了 DBMS_DB_VERSION 常量與條件編譯的結(jié)合使用,。其中對 Oracle 數(shù)據(jù)庫版本和發(fā)布均進(jìn)行了檢查。這個 CHECK_VERSIONS 過程使用 COMMIT WRITE IMMEDIATE NOWAIT 命令(已在 Oracle 數(shù)據(jù)庫 10g 第 2 版中引入),。如果在 Oracle 數(shù)據(jù)庫早期版本中執(zhí)行該過程,,則無法識別該命令。因此,,該過程 DBMS_DB_VERSION 程序包檢查數(shù)據(jù)庫的版本,。如果返回的版本低于 10.2,那么將使用常規(guī)的 COMMIT 提交事務(wù),。當(dāng)在版本為 10.2 或更高版本的數(shù)據(jù)庫中執(zhí)行該過程時,,將使用 COMMIT WRITE IMMEDIATE NOWAIT 命令完成該事務(wù)。
1. |
現(xiàn)在可以測試 Oracle 數(shù)據(jù)庫版本了,。從終端窗口中執(zhí)行以下腳本:
@check_version
check_version.sql 腳本包含以下內(nèi)容:
CREATE OR REPLACE PROCEDURE check_version AS BEGIN -- some code which performs transaction processing ... $if DBMS_DB_VERSION.VER_LE_10_2 $then -- traditional commit COMMIT; DBMS_OUTPUT.PUT_LINE (‘The transaction has been successfully committed.‘); $else -- faster COMMIT supported in 10.2 COMMIT WRITE IMMEDIATE NOWAIT; DBMS_OUTPUT.PUT_LINE (‘The transaction has been successfully committed.‘); $end END; /
|
2. |
現(xiàn)在,,在 SQL*Plus 窗口中執(zhí)行以下命令來運行 check_version 過程。
exec check_version
quit
check_version 的輸出說明:無論版本如何,,該過程都能成功完成,,而且對最終用戶而言是透明的。因此,,沒有理由在不同的數(shù)據(jù)庫版本中使用不同的程序單元,。
|
返回子主題
以下示例演示了如何使用 INDICES OF 和 VALUES OF 關(guān)鍵字在 PL/SQL 中進(jìn)行批量綁定,。
當(dāng)以編程方式對記錄集合(緊密集合)進(jìn)行驗證,并從該集合中刪除無效記錄(不符合指定標(biāo)準(zhǔn)的記錄)時,,可以使用 INDICES OF 關(guān)鍵字。這會產(chǎn)生一個有效元素的稀疏集合,,隨后必須將該集合批量插入表中,。通過使用 INDICES OF 關(guān)鍵字,避免了丟失記錄異常的發(fā)生,。
當(dāng)必須將記錄集合(稀疏或緊密集合)根據(jù)某個可以或不可以復(fù)制某些記錄的條件復(fù)制到一個或多個集合變量,,然后將這些記錄插入到表中時,可以使用 VALUES OF 關(guān)鍵字,。使用“VALUES OF”語法,,結(jié)合其元素均為原始集合內(nèi)選定記錄的指針的指針數(shù)組,可以高效地完成此任務(wù),。這降低了創(chuàng)建多個數(shù)據(jù)副本的需要,。異常處理按每個指針記錄執(zhí)行,這意味著如果兩個或多個指針記錄指向同一“原始”數(shù)據(jù),,則報告的任何異常將指示迭代號(指針元素號),。
本示例使用 EMPLOYEES 表構(gòu)建一個稀疏的員工集合。
1. |
必須先根據(jù)現(xiàn)有的 EMPLOYEES 表(在 hr 模式中)創(chuàng)建一個 NEW_EMPLOYEES 表,,并為 EMPLOYEE_ID 列創(chuàng)建一個唯一索引,。這將在插入重復(fù)記錄時生成異常。從終端窗口中執(zhí)行以下命令:
sqlplus hr/hr
@setup
setup.sql 腳本包含以下內(nèi)容:
drop table new_employees;
create table new_employees as select * from employees where 1=2;
create unique index new_employees_employee_id
on new_employees (employee_id);
|
2. |
然后,,創(chuàng)建 P_BULK_BIND 程序包,。該程序包包含使用三種不同的方法創(chuàng)建稀疏集合并將記錄批量插入到 NEW_EMPLOYEES 表中的各種過程。
@cr_p
單擊 cr_p.sql 查看該腳本中包含的內(nèi)容,。
|
3. |
為了解批量綁定如何在 Oracle 數(shù)據(jù)庫 10g 中工作,,我們先看一下它是如何在 Oracle9i 中工作的。FORALL 語句使用 SAVE EXCEPTIONS 語法抑制因為缺失記錄(稍后在 EXCEPTION 塊中打?。┒l(fā)的異常,。執(zhí)行下列命令:
set serverout on size 100000
exec p_bulk_bind.Bulk_Insert_Pre_10g (i_make_sparse => true)
-- this code is located in the Bulk_Insert_Pre_10g procedure
...
forall j in g_emp_recs.First()..g_emp_recs.Last()
save exceptions
insert into new_employees values g_emp_recs(j);
exception when bulk_errors then
for j in 1..sql%bulk_exceptions.Count()
loop
Dbms_Output.Put_Line ( ‘Error from element #‘ ||
To_Char(sql%bulk_exceptions(j).error_index) || ‘: ‘ ||
Sqlerrm(SQL%bulk_exceptions(j).error_code) );
end loop;
...
從輸出中,您看到每個缺失的記錄都生成了一個異常 (ORA-22160),。正如前面指出的,,即使通過 SAVE EXCEPTIONS 語法處理了剩余記錄,但如果已刪除的記錄數(shù)很大,,那么這也將導(dǎo)致性能降低,。
|
4. |
下一步是使用 Oracle 數(shù)據(jù)庫 10g INDICES OF 語法忽略缺失的記錄,從而獲得更好的性能,。執(zhí)行下列命令:
exec p_bulk_bind.Bulk_Insert_With_Indices_Of (i_cause_exception => false)
在此情況下,,布爾輸入值用于忽略負(fù)責(zé)創(chuàng)建異常條件(您將在下個步驟中看到)的代碼塊,。
-- this code is located in the Bulk_Insert_With_Indices_Of
-- procedure
...
forall j in indices of g_emp_recs
save exceptions
insert into new_employees values g_emp_recs(j);
...
這次沒有因缺失記錄而引發(fā)異常。
|
5. |
也可以通過將輸入?yún)?shù)集設(shè)置為 true 來運行前面的過程,。這將導(dǎo)致在插入時生成一個異常,;必須捕獲該異常(通過 SAVE EXCEPTIONS)并稍后對其進(jìn)行處理。導(dǎo)致異常的條件通過一個過程調(diào)用(將姓氏為“Ernst”和“Urman”的員工的 EMAIL 列取空)進(jìn)行模擬,。由于 EMAIL 列有一個 NOT NULL 限制,,因此這將導(dǎo)致在插入兩個相應(yīng)的記錄時生成異常。該異常稍后從 SQL%BULK_EXCEPTIONS 集合中處理,。執(zhí)行下列命令:
exec p_bulk_bind.Bulk_Insert_With_Indices_Of (i_cause_exception => true)
-- this code is located in the exception area
-- in the Bulk_Insert_With_Indices_Of procedure
...
forall j in indices of g_emp_recs
-- between g_emp_recs.First() and g_emp_recs.Last()
-- optional
save exceptions
insert into new_employees values g_emp_recs(j);
exception when bulk_errors then
declare
v_iteration pls_integer;
n pls_integer;
k pls_integer;
begin
for j in 1..sql%bulk_exceptions.Count()
loop
v_iteration := SQL%bulk_exceptions(j).error_index;
Dbms_Output.Put_Line (
‘Error on the ‘ || To_Char(v_iteration)
|| ‘th iteration‘ );
...
注意:錯誤針對迭代號進(jìn)行報告,。這是 Oracle 數(shù)據(jù)庫 10g 中一個新的異常處理功能。
|
6. |
另一個方法是使用 VALUES OF 子句創(chuàng)建第二個指向原始數(shù)組元素的指針集合,。執(zhí)行下列命令:
exec p_bulk_bind.Bulk_Insert_With_Values_Of -
(i_null_email => true, i_violate_pk => false)
Point_To_Sparse 過程用于根據(jù)與前面相同的邏輯創(chuàng)建一個指針集合,。也就是說,它指向包含姓氏中每個字母的第一個匹配項的元素,。該過程從 Bulk_Insert_With_Values_Of 過程中調(diào)用,。
-- this code is located in the Bulk_Insert_With_Values_Of
-- procedure
...
forall j in values of g_values_of_tab
save exceptions
insert into new_employees values g_emp_recs(j);
...
此結(jié)果與第 5 步中的結(jié)果相同:第五個和第十九個迭代引發(fā)異常。
|
7. |
要查看報告迭代號(或指針元素號)的效果(與報告錯誤記錄索引相對),,可以在 i_violate_pk => true 的情況下運行前面的過程,。為了模擬錯誤條件,使用 Cause_Exception_For_Values_Of 過程將邊界集合 (G_EMP_RECS) 中的重復(fù)索引條目插入到指針集合 (G_VALUES_OF_TAB) 中,。因此,,這違反了唯一鍵約束(在第 1 步中創(chuàng)建),并在批量插入到 NEW_EMPLOYEES 表時引發(fā)異常,。執(zhí)行下列命令:
exec p_bulk_bind.Bulk_Insert_With_Values_Of -
(i_null_email => false, i_violate_pk => true)
-- this code is located in the Bulk_Insert_With_Values_Of
-- procedure
...
exception when bulk_errors then
declare
v_iteration pls_integer;
begin
for j in 1..sql%bulk_exceptions.Count()
loop
v_iteration := SQL%bulk_exceptions(j).error_index;
Dbms_Output.Put_Line (
‘Error on the ‘ || To_Char(v_iteration) ||
‘th iteration‘ );
-- Find the index of the offending element
-- from the iteration number
Dbms_Output.Put_Line (
‘last_name for error element: ‘ ||
g_emp_recs(g_values_of_tab(v_iteration)).last_name );
Dbms_Output.Put_Line (
‘Error was: ‘ ||
Sqlerrm(SQL%bulk_exceptions(j).error_code) );
end loop;
end;
...
|
返回主題列表
在本教程中,,您學(xué)習(xí)了如何:
|
如何執(zhí)行 PL/SQL 條件編譯 |
|
了解如何放寬 DBMS_OUTPUT PL/SQL 程序包的總體限制。 |
|
了解批量綁定特性 |
|