資料庫複習10——PL/SQL
資料庫複習
CH10 PL/SQL
10.1 PL/SQL簡介
PL/SQL是Oracle對SQL的過程化的擴充套件,PL/SQL可以實現SQL相關的過程化程式,並且能夠以儲存過程和函式的方式讓一段SQL業務邏輯駐留在SQL伺服器中,以便減少客戶機計算任務並減少網路I/O
10.2 PL/SQL程式設計基礎
(1)簡介
PL/SQL程式設計框架為:
DECLARE
<Variable List>
BEGIN
<Extented SQL Execution>
EXCEPTION
<Exception Handler>
END
若是儲存過程或函式,首部換成Create Procedure/Function ... IS <Variable List>
(見後例)
先看一個完整的PL/SQL程式例子:
DECLARE
name varchar2(20);
BEGIN
Select sname Into name From Student Where s#='001'; DBMS_OUTPUT.PUT_LINE('學號001的學生姓名是:' || sname)
EXCEPTION
When NO_DATA_FOUND Then
DBMS_OUTPUT.PUT_LINE('學號為001的學生不存在');
When others Then
DBMS_OUTPUT.PUT_LINE('發生了其它錯誤');
END;
其中:
name varchar2(20)
宣告一個型別為varchar2
長度為20的變數nameselect S into V
是擴充套件的select語句,把表的欄位S賦值給變數VDBMS_OUTPUT.PUT_LINE()
是PL/SQL標準輸出語句when <Exception> then <Extented SQL Execution>
是PL/SQL異常處理的分支判斷
(2)變數宣告
變數宣告分成普通宣告、表字段型別宣告和記錄型別宣告
普通宣告name varchar2(20);
等價於表字段型別宣告name Student.sname%TYPE
,後者利用了某表的某欄位的型別來宣告變數的型別
記錄型別相當於結構體,提高程式可讀性,如定義stu記錄型別:
TYPE stu IS RECORD (
S# varchar2(10),
name varchar2(20),
age number
);
val stu;
引用時用點運算:val.name
(3)分支與迴圈
1.if分支
If <Condition> Then
<Statement>
Elseif <Condition> Then
<Statement>
Else
<Statement>
End If;
注意=
是相等,:=
是賦值
2.while迴圈
While <Condition> Loop
<Statement>
End LOOP;
3.for迴圈
For <Loop Variable> In [Reverse] <begin>..<end> Loop
<Statement>
End LOOP;
4.loop迴圈
無條件的Loop,必須在迴圈體內部加入退出語句Exit;
或Exit When <Condition>
才能退出該迴圈
(4)異常處理
標準格式:
Exception
When <exception_name_1> Or <exception_name_2> Then
<Statement>
...
When Others Then
<Statement>
系統定義的exception_name包括:
- NO_DATA_FOUND:select into語句未找到匹配元組
- TOO_MANY_ROWS:select into返回多行資料
- VALUE_ERROR:賦值錯誤(型別不匹配、長度過長)
- ZERO_DIVIDE:除零
- TIMEOUT_ON_RESOURCE:等待資源超時
可以通過Raise <exception>
生成一個自定義的異常,然後在Exception段捕捉它;也可以raise_application_error
生成一個程式錯誤,由外部捕捉
10.3 遊標
PL/SQL每次只能處理單個元組,為了使其和SQL**多元組處理的特性保持一致,引入遊標**
遊標是客戶機上用來存放SQL語句返回中間結果的一塊記憶體,目的是為了協調PL/SQL和SQL間資料處理數目的矛盾
(1)宣告與開啟關閉
PL/SQL中游標只能儲存select
語句的中間結果,其宣告如下:
Declare
Cursor <cursor_name> IS <Select Statement>;
遊標宣告時不會立即執行,需要顯式呼叫open
語句(對應關閉遊標的close
語句):
Begin
Open <cursor_name>;
(2)迴圈讀取
遊標中有一下幾個引數用於輔助控制遊標讀取資料:
- %FOUND:布林,當前
Fetch
返回一行時為真 - %NOTFOUND:布林,當前
Fetch
沒有返回一行時為真 - %ISOPEN:布林,遊標已開啟為真
- %ROWCOUNT:數值,返回已從遊標中取出的元組數目
那麼利用Fetch into
語句(返回一條元組)有一下兩種方式完成遊標資料訪問:
1.While迴圈版本
Begin
Open <cursor_name>;
Fetch <cursor_name> into <record_type_variable>;
While <cursor_name>%FOUND Loop
DBMS_OUTPUT.PUT_LINE(...);
Fetch <cursor_name> into <record_type_variable>;
End Loop;
Close <cursor_name>;
...
2.For迴圈版本
Begin
For <Loop_variable> in <cursor_name> Loop
DBMS_OUTPUT.PUT_LINE(...);
End Loop;
...
其中有:
Fetch into
語句返回一條元組,cursor自動指向下一個元組For in
可以簡潔的遍歷遊標內資料,迴圈開始前自動開啟遊標讀取資料,迴圈結束自動關閉遊標,並且自動為<Loop_variable>
宣告記錄型別存放
(3)帶引數的遊標
遊標可以新增引數,如可以在引數中限定where
子句的條件:
Cursor cs_s(val Number(3)) IS Select * from stu where age = val;
10.4 儲存過程和函式
一般匿名的PL/SQL程式每次執行時都需要編譯,而命名PL/SQL程式如儲存過程、函式和觸發器等則是編譯好駐留在資料庫中,可以隨時被SQL或其他PL/SQL程式呼叫
過程和匿名的PL/SQL程式只是宣告格式上些許不同:
Create [or Replace] Procedure <procedure_name> (
<param_name> In|Out|In Out <type> [:= <initial_value>],
...
)
AS|IS
<Variable Declaration>
Begin
<PL/SQL statement>
Exception
<Exception Handler>
End;
)
其中有:
- In引數不能修改,Out引數只能賦值
- 引數不能指定長度,但可以用%TYPE
- 引數傳遞預設按位置順序傳遞,也可以顯式地用
<param_name> => <param_value>
按名稱亂序傳遞 - 函式是有返回型別的儲存過程,在
AS
前加上返回型別宣告Return <return_type>
然後再函式體中用return <return_value>
返回對應型別的值,在其他的PL/SQL中按函式名傳參呼叫,並用:=
賦值給其他變數
10.5 觸發器
觸發器是一類繫結在表上,由特定DML語句(Update、Insert、Delete)觸發自動執行的一段PL/SQL**儲存過程**,一個表能有多個觸發器但一個觸發器只能繫結在一個表上
觸發器的概念在先前的SQL複習中已經詳細講解,這裡列出PL/SQL中觸發器宣告規則:
Create [Or Replace] Trigger <trigger_name>
// 宣告觸發動作與觸發時間
Before|After Delect|Insert|Update [Of <attribute_name>]
// Or 連線多個觸發動作時間宣告
{Or Before|After Delect|Insert|Update [Of <attribute_name>]}*
On <table_name>
// 不寫表示語句觸發,寫For Each Row表示行觸發
[For Each Row]
Declare
...