PL/SQL是Oracle在標準SQL語言上的過程性擴展,它允許嵌入SQL語句、定義變量和常量、使用過程語言結(jié)構(gòu)(條件分支語句和循環(huán)語句)、使用異常來處理Oracle錯誤等。在任何運行Oracler的平臺上,應(yīng)用開發(fā)人員都可以使用PL/SQL。通過使用PL/SQL,可以在一個PL/SQL塊中包含多條SQL語句和PL/SQL語句。PL/SQL可以用于創(chuàng)建存儲過程、觸發(fā)器和程序包等,也可以用于處理業(yè)務(wù)規(guī)則、數(shù)據(jù)庫事件或為SQL命令的執(zhí)行添加程序邏輯。
PL/SQL是一種可移植的高性能事務(wù)處理程序,它支持SQL和面向?qū)ο缶幊蹋峁┝肆己玫男阅芎透咝У奶幚砟芰Γ哂幸韵?個方面的優(yōu)點。
1.提高應(yīng)用程序的運行性能
在編寫OracoeOracle數(shù)據(jù)庫應(yīng)用程序時,開發(fā)人員可以直接將PL/SQL塊內(nèi)嵌到應(yīng)用程序中,其最大的優(yōu)點是可以降低網(wǎng)絡(luò)開銷、提高應(yīng)用程序性能。對于其他異構(gòu)數(shù)據(jù)庫(例如SQL Server、Sybase、DB2等),當應(yīng)用程序訪問RDBMS時,每次只能發(fā)送單條SQL語句。而對于Oracle數(shù)據(jù)庫而言,通過使用PL/SQL塊,可以將多條SQL語句組織到同一個PL/SQL塊中,從而降低網(wǎng)絡(luò)開銷,提高應(yīng)用程序的性能。
2.提供模塊化的程序設(shè)計功能
當開發(fā)數(shù)據(jù)庫應(yīng)用程序時,為了簡化客戶端應(yīng)用程序的開發(fā)和維護工作,可以首先將企業(yè)規(guī)則或商業(yè)邏輯集成到PL/SQL子程序(過程、函數(shù)和包)中,然后在應(yīng)用程序中調(diào)用子程序?qū)崿F(xiàn)相應(yīng)的程序功能。
3.允許定義標識符
當使用PL/SQL開發(fā)應(yīng)用模塊時,為了使應(yīng)用模塊與應(yīng)用環(huán)境實現(xiàn)數(shù)據(jù)交互,需要定義變量、常量、游標等各種標識符。例如,函數(shù)get_sal中的no為輸入?yún)?shù),用于接收雇員編號的輸入值,而salary變量則用于臨時存儲雇員工資。
4.具有過程語言控制結(jié)構(gòu)
PL/SQL是Oracle在標準SQL上的過程性擴展,它不僅允許在PL/SQL塊中嵌入SQL語句,而且允許在PL/SQL塊中使用各種類型的條件分支語句和循環(huán)語句。
5.具有良好的兼容性
PL/SQL是Oracle提供的用于實現(xiàn)應(yīng)用模塊的語言,在允許運行Oracle的任何平臺上都可以使用PL/SQL。例如,在Oracle數(shù)據(jù)庫中使用PL/SQL開發(fā)數(shù)據(jù)庫端的過程、函數(shù)和觸發(fā)器,以及在Oracle提供的應(yīng)用開發(fā)工具Developer中使用PL/SQL開發(fā)客戶端的過程、函數(shù)和觸發(fā)器。
6.處理運行錯誤
當設(shè)計并開發(fā)應(yīng)用程序時,為了提高應(yīng)用程序的健壯性,可以使用PL/SQL提供的異常處理(EXCEPTION)集中處理各種Oracle錯誤和PL/SQL錯誤,從而簡化錯誤處理。
所有的PL/SQL程序都以塊作為基本單位組成。塊中包含過程化語句和SQL的DML語句。這些塊可以按順序出現(xiàn),也可以相互嵌套(一個塊在別一個塊的內(nèi)部)。按是否帶有名稱以及在數(shù)據(jù)庫中的存儲方式,塊可以分為以下5種。
1.匿名塊
匿名塊是出現(xiàn)在應(yīng)用程序中的沒有名稱且不存儲到數(shù)據(jù)庫中的塊。匿名塊可以出現(xiàn)在SQL語句出現(xiàn)的地方,它們可以調(diào)用其他程序,卻不能被其他程序調(diào)用。
2.命名塊
命名塊是一種帶有標簽的匿名塊,標簽為塊指定了一個名稱。
3.子程序
子程序是存儲在數(shù)據(jù)庫中的過程(PRocedure)、函數(shù)(function),生成之后可以被多次執(zhí)行。
4.程序包
程序包是存儲在數(shù)據(jù)庫中的一組子程序、變量定義。程序包中的子程序可以被其他程序包或者子程序調(diào)用。如果聲明為局部子程序,則只能在定義該局部子程序的塊中被調(diào)用該局部子程序。
5.觸發(fā)器
觸發(fā)器是一種存儲在數(shù)據(jù)庫中的命名塊,生成之后可以被多次執(zhí)行。在相應(yīng)的觸發(fā)器事件發(fā)生之前或之后就會被執(zhí)行一次或多次(每行記錄一次)。觸發(fā)器事件一般是指對特定的數(shù)據(jù)庫表、視圖進行的操作,如INSERT、UPDATE和DELETE等(被稱為DML觸發(fā)器);或者對數(shù)據(jù)庫級的操作,如關(guān)閉、啟動、登錄、退出數(shù)據(jù)庫,創(chuàng)建對象、修改對象、刪除對象等(稱為系統(tǒng)觸發(fā)器)。
PL/SQL程序塊由3個部分組成:定義部分、執(zhí)行部分和異常處理部分。其中,定義部分用于定義常量、變量、游標、異常和復(fù)雜數(shù)據(jù)類型等;執(zhí)行部分用于實現(xiàn)應(yīng)用模塊功能,該部分包含需要執(zhí)行的PL/SQL語句和SQL語句;異常處理部分用于處理執(zhí)行部分可能出現(xiàn)的運行錯誤。PL/SQL塊的基本結(jié)構(gòu)如下:
DECLARE
定義部分
BEGIN
執(zhí)行部分
EXCEPTION
異常處理部分
END;
其中,定義部分以DECLARE開始,該部分是可選的;執(zhí)行部分以BEGIN開始,該部分是必須的;異常處理部分以EXCEPTION開始,該部分是可選的;END則是PL/SQL程序塊的結(jié)束標記。以下示例創(chuàng)建匿名程序塊,用于接收用戶輸入的員工編號,輸出該員工的姓名,以及處理用戶輸入的員工編號不存在的異常
注意
DECLAREE、BEGIN和EXCEPTION后面沒有“;”(分號),而END后則必須要帶“;”(分號)。
在PL/SQL程序中加入注釋可以幫助理解程序,PL/SQL編譯器在編譯時會忽略注釋,PL/SQL的注釋分為單行注釋和多行注釋。
1.單行注釋
單行注釋可以放在一行的任何地方,由兩條短橫線(--)開始。
2.多行注釋
多行注釋“/*”開始、“*/”結(jié)束,可以跨越多行,但不允許嵌套。
在調(diào)試PL/SQL程序時,完全可以使用單行或多行注釋來將暫時不需要或不正確的語句進行注釋或禁用。
在聲明部分中可以聲明需要使用的常量、變量、函數(shù)、游標、異常處理名稱等。
1.聲明
聲明常量、變量的語法如下:
語法:
Iidentifier_name [CONSTANT] data_type [NOTNULL][:=value_expression]|[DEFAULT value_expression]
其中,identifier_name指定需要聲明的常量、變量的名稱;data_type指定數(shù)據(jù)類型;“:=”是賦值運算符(或使用DEFAULT);value_expression是賦值表達式。如果語句中有CONSTANT,則表明聲明的是一個常量;如果有NOT NULL,則表明聲明的變量是不能為空(即在聲明時必須賦值)。聲明常量時應(yīng)該立即賦值,如果沒有賦值則表示初始化為NULL。
注意:
在PL/SQL中,每一行中能聲明一個常量或變量。在引用一個常量或變量之前,必須先對其進行聲明。
2.使用SELECT INTO語句為變量賦值
除了可以使用常量為變量賦值之外,還可以將從數(shù)據(jù)庫表中查詢獲得的值賦給變量。通過SELECT INTO語句可以從數(shù)據(jù)庫表中查詢的結(jié)果賦予變量,根據(jù)雇員編號獲得雇員工資、補助和總工資,代碼如圖4.1.1所示。該示例代碼中,從數(shù)據(jù)庫中根據(jù)員工編號查詢員工姓名,并把員工姓名賦給變量v_ename。
編寫PL/SQL程序時,若臨時存儲數(shù)值,則必須定義變量和常量;若在應(yīng)用環(huán)境和子程序之間傳遞數(shù)據(jù),則必須為子程序指定參數(shù)。而在PL/SQL程序中定義變量、常量和參數(shù)時,必須為它們指定PL/SQL數(shù)據(jù)類型。常見的數(shù)據(jù)類型如表14-1-1所示。
表14-1-1 部分數(shù)據(jù)類型及說明
數(shù)據(jù)類型 | 說明 |
char | 表示固定長度字符串,長度不夠時使用空格來補充,最多可以存儲2000字節(jié)。 |
varchar2 | 表示可變長字符串,最多可以存儲4000字節(jié)。 |
number | 可以存儲正數(shù)、負數(shù)、零、定點數(shù)和精度為38的浮點數(shù)。其格式為:number(m,n),其中m表示精度代表數(shù)字的總位數(shù);n表示小數(shù)點右邊數(shù)字的位數(shù)。 |
date | 用于存儲表中的日期和時間數(shù)據(jù),取值范圍是公元前4712年1月1日到公元9999年12月31日。Date類型的長度是7,7個字節(jié)分別表示世紀、年、月、日、時、分和秒。 |
timestamp | 用戶存儲日期的年、月、日、小時、分和秒值。其中,秒值精確到小數(shù)點后6位數(shù),該數(shù)據(jù)類型同時包含時區(qū)信息。 |
clob | 用于存儲可變長度的字符數(shù)據(jù),最多可存儲4GB。該數(shù)據(jù)類型用于存儲varchar2類型不能存儲的長文本信息。 |
blob | 用于存儲較大的二進制對象,如圖形、視頻剪輯和聲音剪輯等,該類型最多可存儲4GB數(shù)據(jù)。 |
PL/SQL中的數(shù)據(jù)類型除了表1-1-1所列出的以外還有以下常用的類型:
1.%TYPE
當定義PL/SQL變量存放值時,必須確保變量使用合適的數(shù)據(jù)類型和長度,否則可能在運行過程中出現(xiàn)PL/SQL運行錯誤。此時,可以使用%TYPE屬性來定義變量。當使用%TYPE屬性定義變量時,Oracle會自動地按照數(shù)據(jù)庫列或其他變量來確定新變量的類型和長度。圖4.1.3演示了使用%TYPE定義變量的過程。
2.%ROWTYPE
如果一張表中包含較多的列,則可以使用%ROWTYPE來定義一個表示表中一行記錄的變量,這樣比分別使用%TYPE來定義表示表中各個列的變量理更加簡潔。為了使一個變量的數(shù)據(jù)類型與一張表中記錄的各個列的數(shù)據(jù)類型對應(yīng)一致,Oracle提供了%ROWTYPE定義定議方式。
在圖4.1.3中,根據(jù)輸入的員工編號查詢該員工姓名、部門編號,并將查詢結(jié)果存儲在變量v_ename、v_deptno中。然后根據(jù)根據(jù)部門編號查詢一條部門信息并將該信息存入變量v_dept_row中。
3.RECORD
PL/SQL記錄類似于高級語言中的結(jié)構(gòu),每個PL/SQL記錄一般都包含多個成員。當使用PL/SQL記錄時,首先需要在定義部分定義記錄類型和記錄變量,然后在執(zhí)行部分引用該記錄變量。當引用記錄成員時,必須將記錄變量作為前綴,格式為“記錄變量.記錄成員”。
語法:
Type record_name is record
(
V1 data_type1 [not null] [:=default_value],
V2 data_type2 [not null] [:=default_value],
Vndata_typen [not null] [:=default_value]
);
4.TABLE
索引表相當于一個鍵值集合,鍵是唯一的,用于查找對應(yīng)的值。鍵可以是整數(shù)或字符串。
語法:
Type table_name is table of element_type [not null]
index by [binary_integer | pls_integer |varchar2]
關(guān)鍵字INDEX BY表示創(chuàng)建一個主鍵索引,以便引用記錄表變量中的特定行。
流程控制是PL/SQL對SQL最重要的擴展,流程控制結(jié)構(gòu)包括條件控制、循環(huán)控制和順序控制。
在許多情況中,需要按照某種條件來選擇執(zhí)行某些語句段。條件控制先測試一個條件,然后根據(jù)測試的結(jié)果選擇、運行不同的語句段。條件結(jié)構(gòu)中允許嵌套。
1.IF…THEN…END IF結(jié)構(gòu)
最簡單的條件控制結(jié)構(gòu)。
語法:
If IF 條件表達式 THEN
語句段
END IF;
2.IF…THEN…ELSE…END IF結(jié)構(gòu)
二重條件分支結(jié)構(gòu)。如果滿足條件則執(zhí)行一組操作,不滿足條件則執(zhí)行另外一級組操作。
語法:
IF 條件表達式 THEN
語句段1
ELSE
語句段2
END IF;
3.IF…THEN…ELSIF…END IF結(jié)構(gòu)
多重條件分支結(jié)構(gòu)。如果滿足第一個條件,則執(zhí)行第一種操作;如果不滿足第一個條件,則檢查是否滿足第二個條件,如果滿足則執(zhí)行第二種操作;如果不滿足第二個條件,則檢查是否滿足第三個條件……依此類推。
語法:
IF 表達式1 THEN
語句段1
ELSIF 條件表達式2 THEN
語句段2
……
ELSIF 條件表達式n THEN
語句段n
ELSE
語句段
END IF ;
CASE結(jié)構(gòu)
CASE結(jié)構(gòu)是在Oracle 9i中引的另一種邏輯判斷結(jié)構(gòu),它提供了另一種邏輯判斷的實現(xiàn)方法。
語法:
CASE 表達式
WHEN 條件表達式結(jié)果1 THEN
語句段1;
WHEN 條件表達式結(jié)果2 THEN
語句段2;
…...
ELSE
語句段n;
END CASE;
CASE結(jié)構(gòu)的另一種語法格式被稱為CASE搜索結(jié)構(gòu),語法如下:
語法:
CASE
WHEN 條件表達式結(jié)果1 THEN
語句段1;
WHEN 條件表達式結(jié)果2 THEN
語句段2;
……
ELSE
語句段n;
END CASE;
其中,CASE結(jié)構(gòu)非常適用于從多個值中選擇一個,以決定需要執(zhí)行的語句段。在實際應(yīng)用中,CASE結(jié)構(gòu)的可讀性及有效性更高,盡量將很長的IF…THEN…ELSIF…END IF語句改寫成CASE結(jié)構(gòu)。
為了在編寫的PL/SQL塊中重復(fù)執(zhí)行一條語句或者一組語句,可以使用循環(huán)控制結(jié)構(gòu)。編寫循環(huán)控制結(jié)構(gòu)時,用戶可以使用基本循環(huán)、WHILE循環(huán)和FOR循環(huán)等類型的循環(huán)語句。
1.基本循環(huán)
在PL/SQL中,格式最簡單的循環(huán)語句是基本循環(huán)語句,它以LOOP開始,以END LOOP結(jié)束。
語法:
LOOP
語句段;
EXIT [WHEN 條件表達式]
END LOOP;
上述語法中,當使用基本循環(huán)時,無論是否滿足條件,語句至少會被執(zhí)行一次。當條件表達式為TRUE時,會退出循環(huán),并執(zhí)行END LOOP后的操作。
注意:
EXIT語句必須放在循環(huán)體內(nèi),且只能退出循環(huán)體,不能退出PL/SQL塊。當需要退出PL/SQL塊時,應(yīng)該使用RETURN語句。
WHILE循環(huán)
基本循環(huán)至少需要執(zhí)行一次循環(huán)體內(nèi)的語句,而WHILE循環(huán)中,只有當條件為TRUE時,才會執(zhí)行循環(huán)體內(nèi)的語句。我們改造圖4.1.9的示例,使用while循環(huán)來實現(xiàn)
FOR循環(huán)
當使用基本循環(huán)或WHILE循環(huán)時,需要定義循環(huán)控制變量。循環(huán)控制變量不僅可以使用NUMABENUMBER類型,還可以使用其他數(shù)據(jù)類型。當使用FOR循環(huán)時,Oracle會隱含定義循環(huán)變量。
默認情況下,當使用FOR循環(huán)時,每次循環(huán),時控制變量會自動增加1;如果指定了REVERSE選項,則每次循環(huán)時循環(huán)控制變量會自動減少1。在循環(huán)體內(nèi)部可以引用循環(huán)變量,但不能對其賦值。
PL/SQL異常是指通過編譯的PL/SQL程序在運行時產(chǎn)生的錯誤。導(dǎo)致異常的原因較多,如內(nèi)存用盡、硬件故障、違反表的完整性約束、設(shè)計缺陷等。PL/SQL提供了異常錯誤處理機制,可以幫助實現(xiàn)對錯誤的捕獲和處理。當異常發(fā)生時,PL/SQL會自動地捕獲異常并將程序控制流程轉(zhuǎn)移到異常處理部分部會的程序。
語法:
EXCEPTION
WHEN 異常錯誤名稱1 [OR 異常錯誤名稱2……] THEN
語句段1;
WHEN 異常錯誤名稱3 [OR 異常錯誤名稱4…… ] THEN
語句段2;
……
WHEN OTHERS THEN
語句段3;
上述語法中,異常處理部分以保留字EXCEPTION開始,可以使用WHEN子句按“異常錯誤名稱”捕獲各種異常錯誤,其他沒有預(yù)計到的異常錯誤可以使用WHEN OTHERS子句進行捕獲。
針對一些常見的錯誤,PL/SQL預(yù)定義了一些異常錯誤。當PL/SQL程序違反了Oracle的規(guī)定或超出了系統(tǒng)規(guī)定的限制時,就會隱含地引發(fā)一個預(yù)定義的錯誤。PL/SQL在程序包STANDARD中包含了這些預(yù)定義異常,常用的預(yù)定義異常錯誤見表4-1-2所示。
表1.1.14-1-2 PL/SQL預(yù)定義異常
異常 | ORACLE錯誤 | 描述 |
access_into_null | ora-6530 | 試圖訪問一個未初始化的對象 |
case_not_found | ora-6592 | case語句中的選項與用戶輸入的數(shù)據(jù)不匹配 |
cursor_already_open | ora-6511 | 試圖打開一個已打開的游標 |
dup_val_on_index | ora-0001 | 試圖破壞一個唯一性約束 |
invalid_cursor | ora-1001 | 試圖打開一個無效的游標 |
invalid_number | ora-1722 | 試圖對非數(shù)字值進行數(shù)字操作 |
login_denied | ora-1017 | 無效的用戶名或者口令 |
no_data_found | ora-1403 | 查詢未找到數(shù)據(jù) |
not_logged_on | ora-1012 | 還未連接就試圖數(shù)據(jù)庫操作 |
program_error | ora-6501 | 內(nèi)部錯誤 |
rowtype_mismatch | ora-6504 | 主變量的游標的類型不兼容 |
storage_error | ora-6500 | 內(nèi)部錯誤 |
timeout_on_resource | ora-0051 | 發(fā)生超時 |
too_many_rows | ora-1422 | select into 命令返回多行 |
transaction_backed_out | ora-006 | 由于死鎖提交被退回 |
value_error | ora-6502 | 轉(zhuǎn)換或者裁剪錯誤 |
zero_divide | ora-1476 | 試圖被除零 |
預(yù)定義異常都是由Oracle判斷的異常錯誤。在實際的PL/SQL程序開發(fā)過程中,為了具體的業(yè)務(wù)規(guī)則、編程和調(diào)試的需要,程序員可以自定義一些異常。自定義異常必須進行聲明,并且必須使用RAISE語句顯式地觸發(fā)。
在刪除表中的數(shù)據(jù)時,如果沒有符合條件的記錄,則不會刪除數(shù)據(jù)。因為這不是錯誤,所以不會有任何的錯誤提示,但可以使用自定義錯誤的方法來提示。例如,通過判斷來獲知是否存在該記錄,并使用自定義異常的方法進行提示,代碼如圖14.1.13所示。其中sql代表隱含游標。
設(shè)想網(wǎng)上購物的一次交易,其付款過程至少包括以下幾步數(shù)據(jù)庫操作:
? 更新客戶所購商品的庫存信息
? 保存客戶付款信息--可能包括與銀行系統(tǒng)的交互
? 生成訂單并且保存到數(shù)據(jù)庫中
? 更新用戶相關(guān)信息,例如購物數(shù)量等等
正常的情況下,這些操作將順利進行,最終交易成功,與交易相關(guān)的所有數(shù)據(jù)庫信息也成功地更新。但是,如果在這一系列過程中任何一個環(huán)節(jié)出了差錯,例如在更新商品庫存信息時發(fā)生異常、該顧客銀行帳戶存款不足等,都將導(dǎo)致交易失敗。一旦交易失敗,數(shù)據(jù)庫中所有信息都必須保持交易前的狀態(tài)不變,比如最后一步更新用戶信息時失敗而導(dǎo)致交易失敗,那么必須保證這筆失敗的交易不影響數(shù)據(jù)庫的狀態(tài)--庫存信息沒有被更新、用戶也沒有付款,訂單也沒有生成。否則,數(shù)據(jù)庫的信息將會一片混亂而不可預(yù)測。
數(shù)據(jù)庫事務(wù)正是用來保證這種情況下交易的平穩(wěn)性和可預(yù)測性的技術(shù)。
事務(wù)用于保證數(shù)據(jù)的一致性,它由一組相關(guān)的dml語句組成,該組的dml(數(shù)據(jù)操作語言insert delete update)語句要么全部成功,要么全部失敗。
對一組SQL語句操作構(gòu)成事務(wù),數(shù)據(jù)庫操作系統(tǒng)必須確保這些操作的原子性,一致性,隔離性,持久性.
1、 原子性(Atomicity)
事務(wù)的原子性是指事務(wù)中包含的所有操作要么全做,要么不做,也就是說所有的活動在數(shù)據(jù)庫中要么全部反映,要么全部不反映,以保證數(shù)據(jù)庫的一致性。
2、 一致性(Consistency)
事務(wù)的一致性是指數(shù)據(jù)庫在事務(wù)操作前和事務(wù)處理后,其中數(shù)據(jù)必須滿足業(yè)務(wù)的規(guī)則約束。 和我們的規(guī)則預(yù)期一致。 不會出現(xiàn)一個加 一個沒減
3、 隔離性(Isolation)
隔離性是指數(shù)據(jù)庫允許多個并發(fā)的事務(wù)同時對其中的數(shù)據(jù)進行讀寫或修改的能力,隔離性可以防止多個事務(wù)的并發(fā)執(zhí)行時,由于它們的操作命令交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致性。 Oracle支持 read commited 和序列化
4、 持久性(durability)
事務(wù)的持久性是指在事務(wù)處理結(jié)束后,它對數(shù)據(jù)的修改應(yīng)該是永久的。即便是系統(tǒng)在遇到故障的情況下也不會丟失,這是數(shù)據(jù)的重要性決定的。
在Oracle數(shù)據(jù)庫中,沒有提供開始事務(wù)處理語句,所有的事務(wù)都是隱式開始的,也就是說在oracle中,用戶不可以顯示使用命令來開始一個事務(wù).oracle任務(wù)第一條修改數(shù)據(jù)庫的語句,或者一些要求事務(wù)處理的場合都是事務(wù)的隱式開始。但是當用戶想要終止一個事務(wù)處理時,必須顯示使用commit和rollback語句結(jié)束。
Savepoint 在事務(wù)中建立一個存儲的點.當事務(wù)處理發(fā)生異常而回滾事務(wù)時,可指定事務(wù)回滾到某存儲點.然后從該存儲點重新執(zhí)行。
Rollback 回滾事務(wù) 取消對數(shù)據(jù)庫所作的任何操作
Commit 提交事務(wù) 對數(shù)據(jù)庫的操作做持久的保存。
實例4.14:設(shè)置自動提交
SET AUTOCOMMIT {ON | OFF | IMMEDIATE | n}
IMMEDIATE選項的功能與ON相同
n表示當Oracle數(shù)據(jù)庫成功執(zhí)行了n條insert,update,delete或PL/SQL程序塊時自動提交事務(wù)
SQL> set autocommit on;
SQL>show autocommit;
Commit語句用于提交事務(wù)
使用ROLLBACK語句可以將事務(wù)回滾到事務(wù)的起點或某個保存點開始
定義保存點
保存點定義了事務(wù)中的一個位置,使用Rollback語句可以將數(shù)據(jù)會滾到保存點的位置,從而實現(xiàn)事務(wù)的部分回滾功能。
savepoint <回滾點>
rollback to <回滾點>
查看事務(wù)信息
查看事務(wù)信息使用視圖V$TRANSACTION,這個視圖必須使用sys或system訪問,因為當前事務(wù)是匿名事務(wù),所以name字段值為空
前面的實例都是匿名事務(wù),其實可以給事務(wù)命名
SET TRANSACTION NAME '事務(wù)名稱'
通過plsql塊 演示事務(wù)declarebegin insert into dept values(33,'xx','ss');insert into dept values(10,'xx','ss');commit;exception when others thenrollback;end;4.1.1 事務(wù)和鎖
當多個用戶同時訪問Oracle數(shù)據(jù)庫,執(zhí)行事務(wù)操作(dml語句)時,同時可能有多個用戶訪問同一個數(shù)據(jù)庫資源,Oracle會自動在被作用的表上加鎖,防止其他用戶的事務(wù)改表的數(shù)據(jù),這里對我們用戶來說是非常重要的,它會避免使用的數(shù)據(jù)產(chǎn)生邏輯錯誤。
例如轉(zhuǎn)賬 可能出現(xiàn)負值。
如果沒有鎖定且多個用戶同時訪問一個數(shù)據(jù)庫,則當他們的事務(wù)同時使用相同的數(shù)據(jù)時可能會發(fā)生問題。由于并發(fā)操作帶來的數(shù)據(jù)不一致性包括:丟失數(shù)據(jù)修改、讀”臟”數(shù)據(jù)(臟讀)、不可重復(fù)讀、產(chǎn)生幽靈數(shù)據(jù)。
(1)丟失數(shù)據(jù)修改
當兩個或多個事務(wù)選擇同一行,然后基于最初選定的值更新該行時,會發(fā)生丟失更新問題。每個事務(wù)都不知道其它事務(wù)的存在。最后的更新將重寫由其它事務(wù)所做的更新,這將導(dǎo)致數(shù)據(jù)丟失。如上例。 再例如,兩個編輯人員制作了同一文檔的電子復(fù)本。每個編輯人員獨立地更改其復(fù)本,然后保存更改后的復(fù)本,這樣就覆蓋了原始文檔。最后保存其更改復(fù)本的編輯人員覆蓋了第一個編輯人員所做的更改。如果在第一個編輯人員完成之后第二個編輯人員才能進行更改,則可以避免該問題。
(2)讀“臟”數(shù)據(jù)(臟讀)
讀“臟”數(shù)據(jù)是指事務(wù)T1修改某一數(shù)據(jù),并將其寫回磁盤,事務(wù)T2讀取同一數(shù)據(jù)后,T1由于某種原因被除撤消,而此時T1把已修改過的數(shù)據(jù)又恢復(fù)原值,T2讀到的數(shù)據(jù)與數(shù)據(jù)庫的數(shù)據(jù)不一致,則T2讀到的數(shù)據(jù)就為“臟”數(shù)據(jù),即不正確的數(shù)據(jù)。 例如:一個編輯人員正在更改電子文檔。在更改過程中,另一個編輯人員復(fù)制了該文檔(該復(fù)本包含到目前為止所做的全部更改)并將其分發(fā)給預(yù)期的用戶。此后,第一個編輯人員認為目前所做的更改是錯誤的,于是刪除了所做的編輯并保存了文檔。分發(fā)給用戶的文檔包含不再存在的編輯內(nèi)容,并且這些編輯內(nèi)容應(yīng)認為從未存在過。如果在第一個編輯人員確定最終更改前任何人都不能讀取更改的文檔,則可以避免該問題。
( 3)不可重復(fù)讀
指事務(wù)T1讀取數(shù)據(jù)后,事務(wù)T2執(zhí)行更新操作,使T1無法讀取前一次結(jié)果。不可重復(fù)讀包括三種情況: 事務(wù)T1讀取某一數(shù)據(jù)后,T2對其做了修改,當T1再次讀該數(shù)據(jù)后,得到與前一不同的值。
扣款。
(4)產(chǎn)生幽靈數(shù)據(jù)
按一定條件從數(shù)據(jù)庫中讀取了某些記錄后,T2刪除了其中部分記錄,當T1再次按相同條件讀取數(shù)據(jù)時,發(fā)現(xiàn)某些記錄消失 T1按一定條件從數(shù)據(jù)庫中讀取某些數(shù)據(jù)記錄后,T2插入了一些記錄,當T1再次按相同條件讀取數(shù)據(jù)時,發(fā)現(xiàn)多了一些記錄。
數(shù)據(jù)庫是一個多用戶使用的共享資源。當多個用戶并發(fā)地存取數(shù)據(jù)時,在數(shù)據(jù)庫中就會產(chǎn)生多個事務(wù)同時存取同一數(shù)據(jù)的情況。若對并發(fā)操作不加控制就可能會讀取和存儲不正確的數(shù)據(jù),破壞數(shù)據(jù)庫的一致性。 加鎖是實現(xiàn)數(shù)據(jù)庫并發(fā)控制的一個非常重要的技術(shù)。當事務(wù)在對某個數(shù)據(jù)對象進行操作前,先向系統(tǒng)發(fā)出請求,對其加鎖。加鎖后事務(wù)就對該數(shù)據(jù)對象有了一定的控制,在該事務(wù)釋放鎖之前,其他的事務(wù)不能對此數(shù)據(jù)對象進行更新操作。
使用鎖解決不可重復(fù)讀 和丟失數(shù)據(jù)修改!!
在數(shù)據(jù)庫中有兩種基本的鎖類型:排它鎖(Exclusive Locks,即X鎖)和共享鎖(Share Locks,即S鎖)。當數(shù)據(jù)對象被加上排它鎖時,其他的事務(wù)不能對它讀取和修改。加了共享鎖的數(shù)據(jù)對象可以被其他事務(wù)讀取,但不能修改。數(shù)據(jù)庫利用這兩種基本的鎖類型來對數(shù)據(jù)庫的事務(wù)進行并發(fā)控制。
Oracle數(shù)據(jù)庫的鎖類型
根據(jù)保護的對象不同,Oracle數(shù)據(jù)庫鎖可以分為以下幾大類:DML鎖(data locks,數(shù)據(jù)鎖),用于保護數(shù)據(jù)的完整性;DDL鎖(dictionary locks,字典鎖),用于保護數(shù)據(jù)庫對象的結(jié)構(gòu),如表、索引等的結(jié)構(gòu)定義;內(nèi)部鎖和閂(internal locks and latches),保護數(shù)據(jù)庫的內(nèi)部結(jié)構(gòu)。
DML鎖的目的在于保證并發(fā)情況下的數(shù)據(jù)完整性,本文主要討論DML鎖。在Oracle數(shù)據(jù)庫中,DML鎖主要包括TM鎖和TX鎖,其中TM鎖稱為表級鎖,TX鎖稱為事務(wù)鎖或行級鎖。 粒度
當Oracle執(zhí)行DML語句時,系統(tǒng)自動在所要操作的表上申請TM類型的鎖 用來預(yù)防其他事務(wù)使用ddl語句來修改表結(jié)構(gòu) 刪除表等。當TM鎖獲得后,系統(tǒng)再自動申請TX類型的鎖,并將實際鎖定的數(shù)據(jù)行的鎖標志位進行置位。這樣在事務(wù)加鎖前檢查TX鎖相容性時就不用再逐行檢查鎖標志,而只需檢查TM鎖模式的相容性即可,大大提高了系統(tǒng)的效率。TM鎖包括了SS、SX、S、X等多種模式,在數(shù)據(jù)庫中用0-6來表示。不同的SQL操作產(chǎn)生不同類型的TM鎖。
在數(shù)據(jù)行上只有X鎖(排他鎖)。在 Oracle數(shù)據(jù)庫中,當一個事務(wù)首次發(fā)起一個DML語句時就獲得一個TX鎖,該鎖保持到事務(wù)被提交或回滾。當兩個或多個會話在表的同一條記錄上執(zhí)行DML語句時,第一個會話在該條記錄上加鎖,其他的會話處于等待狀態(tài)。當?shù)谝粋€會話提交后,TX鎖被釋放,其他會話才可以加鎖。
當Oracle數(shù)據(jù)庫發(fā)生TX鎖等待時,如果不及時處理常常會引起Oracle數(shù)據(jù)庫掛起,或?qū)е滤梨i的發(fā)生,產(chǎn)生ORA-60的錯誤。這些現(xiàn)象都會對實際應(yīng)用產(chǎn)生極大的危害,如長時間未響應(yīng),大量事務(wù)失敗等。
示例4.20:死鎖實例
因為大型數(shù)據(jù)庫應(yīng)用系統(tǒng)存在并發(fā)機制,也就是說可能同時有多個會話訪問同一個數(shù)據(jù),這時可能不可避免出現(xiàn)死鎖。例如有兩個事務(wù)A和B,如果事務(wù)A已經(jīng)鎖定了數(shù)據(jù)庫對象A(數(shù)據(jù)庫對象A可能是一張表或表中的幾條記錄)。如果事務(wù)B也已經(jīng)鎖定了數(shù)據(jù)庫對象B(數(shù)據(jù)庫對象B可能是一張表或表中的幾條記錄)。此時事務(wù)A再去申請鎖定數(shù)據(jù)庫對象B,而同時時事務(wù)B再去申請鎖定數(shù)據(jù)庫對象A。兩者互不相讓,就產(chǎn)生了死鎖,嚴重影響性能。
示例4.21:jdbc中的死鎖public static void main(String[] args) { Connection ct = null; try { // 1、加在驅(qū)動 Class.forName("oracle.jdbc.driver.OracleDriver"); // 得到連接 ct = DriverManager .getConnection( "jdbc:oracle:thin:@localhost:1521:orcl","scott","tiger"); // 從下面開始和sql一樣 // 加入事務(wù)處理 ct.setAutoCommit(false); Statement sm = ct.createStatement(); sm.executeUpdate("update dept set loc='shanghai' where deptno=10"); Thread.sleep(4000); sm.executeUpdate("update dept set loc='beijing' where deptno=20"); } catch (Exception e) { e.printStackTrace(); } finally{ System.out.println("事務(wù)1結(jié)束!!!!"); try { ct.commit(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }創(chuàng)建第二個main函數(shù),將Statement類對象sm執(zhí)行的SQL語句調(diào)換一下,就可以模擬死鎖。
也可以設(shè)置隔離級別來達到控制并發(fā)的目的 其實隔離級別的原理就是使用不同的鎖策略來達到目的:
Read commited : 這種隔離級別指的是,事務(wù)只能讀取已經(jīng)提交的數(shù)據(jù),(但是支持可重復(fù)讀與幻讀)是oracle數(shù)據(jù)庫默認的隔離模式。
SEAIALIZABLE:不允許臟讀,重復(fù)讀與幻想讀,最高的隔離級別。這種隔離級別標明事務(wù)A在操作數(shù)據(jù)庫的時候好像就只有事務(wù)A在操作,沒有其他事務(wù)在操作數(shù)據(jù)庫一樣。
Oracle 中是這樣實現(xiàn) SERIALIZABLE 事務(wù)的:原本通常在語句級得到的讀一致性現(xiàn)在可以擴展到事務(wù)級。也就是在事務(wù)執(zhí)行的那一刻,將這個事務(wù)將要操作的數(shù)據(jù)拍了一張照片。
級別越高 并發(fā)性越低 完整性一致性越高。
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
l SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
? 本章總結(jié)
? PL/SQL是Oracle在標準SQL語言上的過程性擴展,它允許嵌入SQL語句、定義變量和常量、使用過程語言結(jié)構(gòu)(條件分支語句和循環(huán)語句)、使用異常來處理Oracle錯誤等
? PL/SQL塊可以分為匿名塊、命名塊、子程序、程序包,觸發(fā)器5種
? 針對一些常見的錯誤,PL/SQL預(yù)定義了一些異常錯誤。當PL/SQL程序違反了Oracle的規(guī)定或超出了系統(tǒng)規(guī)定的限制時,就會隱含地引發(fā)一個預(yù)定義的錯誤
? 為了具體的業(yè)務(wù)規(guī)則、編程和調(diào)試的需要,程序員可以自定義一些異常。自定義異常必須進行聲明,并且必須使用RAISE語句顯式地觸發(fā)。
? 事務(wù)用于保證數(shù)據(jù)的一致性,它由一組相關(guān)的dml語句組成,該組的dml(數(shù)據(jù)操作語言insert delete update)語句要么全部成功,要么全部失敗。
? 事務(wù)控制語句包括commit,rollback,savepoint
任務(wù)實訓(xùn)部分
1:PL/SQL塊中數(shù)據(jù)類型的使用
訓(xùn)練技能點
? PL/SQL塊程序結(jié)構(gòu)中的數(shù)據(jù)類型
需求說明
在使用記錄變量插入數(shù)據(jù)和更新數(shù)據(jù)時,需注意列的順序、個數(shù)、類型必須與記錄成員
的順序、個數(shù)、類型完全匹配。現(xiàn)要求實現(xiàn):使用記錄變量的方式來插入部門信息。
實現(xiàn)思路
(1) 定義一個關(guān)于dept部門表的%ROWTYPE變量。
(2) 為其中的deptno部門編號、部門名稱及地址賦值。
(3) 然后通過INSERT語句插入數(shù)據(jù)。
關(guān)鍵代碼
PL/SQL塊中流程控制語句的使用
訓(xùn)練技能點
? FOR循環(huán)的使用
需求說明
在PL/SQL中,可以使用FOR循環(huán)直接訪問SELECT查詢獲得的結(jié)果。要求查詢出各個部
門工資最高的雇員信息。
實現(xiàn)思路
(1) 通過FOR循環(huán)訪問各個部門,輸出dept表中所有部門的名稱
(2) 根據(jù)各個部門提供的部門編號(deptno)查詢emp表中該部門工資最高的雇員。
(3) 輸出該雇員信息。
通過For循環(huán)查詢獲得各個部門工資最高的雇員信息
異常處理
訓(xùn)練技能點
? 異常處理
需求說明
使用非預(yù)定義異常,更新特定雇員的部門號,并處理ORA-02291錯誤。當定義Oracle錯誤和異常之間的關(guān)聯(lián)關(guān)系時,需要使用偽過程EXCEPTION_INIT。
實現(xiàn)思路
(1) 首先的定義部分定義異常。
(2) 使用progma exception_init(exception_name,exception_number)在異常和oracle錯誤之間建立關(guān)聯(lián),這時要求用戶知道可能出現(xiàn)的錯誤號。
(3) 最終在異常處理部分捕捉并處理異常。
鞏固練習(xí)
一.選擇題
1. 數(shù)據(jù)庫中有兩種基本的鎖類型是( )
A. 排它鎖
B. 共享鎖
C. 只讀鎖
D. 更新鎖
2. 下列選項中,不能直接嵌入PL/SQL塊中的語句有( )。
A.SELECT
B.INSERT
C.CREATE TABLE
D.GRANT
E.COMMIT
3. 當SELECT INTO語句沒有返回行時,觸發(fā)的異常是
時( )。A.TOO_MANY_ROWS
B.VALUE_ERROR
C.NO_DATA_FOUND
D.不會觸發(fā)任何異常
4. 以下PL/SQL塊的輸出結(jié)果是( )
DECLARE
v_1 NUMBER(2);
v_2 NUMBER(3);
BEGIN
v_2:=CASE WHEN v_1 IS NULL THEN 10 ELSE 20 end;
DBMS_OUTPUT.PUT_LINE(‘v_2:’||v_2);
END;
A.10
B.0
C.20
D.語法錯誤
5. 以下PL/SQL塊的輸出結(jié)果是( )
BEGIN
FOR I IN 1..5 LOOP
DBMS_OUTPUT.PUT_LINE(‘*’);
END LOOP;
END;
A.**
B.***
C.*
*
*
D.不會觸發(fā)任何異常
二.
操作題上機練習(xí)由用戶輸入一個雇員編號,要求在emp表中查找是否存在與輸入的雇員編號對應(yīng)的雇員。
1.如果存在,則有以下操作:
(1)輸出“查到此雇員”,并判斷其部門是否為“SALES”部門。
(2)如果是“SALES”部門,則輸出“此雇員屬于SALES部門”,并刪除對應(yīng)的記錄;否則輸出“此雇員不屬于SALES部門,不能刪除!”。
2.如查不存在,則輸出“找不到此雇員!”。
新聞熱點
疑難解答
圖片精選