Oracle之PL/SQL編程
PL/SQL(Procedural Language/SQL,過程語言/SQL)
是結合了Oracel過程語言和結構化查詢語言(SQL)的一種擴展語言。
優點:
(1)PL/SQL具有編程語言的特點,它能把一組SQL語句放到一個模塊中,使其更具模塊化種序的特點。
(2)PL/SQL可以采用過程性語言控制程序的結構。
(3)PL/SQL有自動處理的異常處理機制。
(4)PL/SQL程序塊具有更好的可移植性,可移植到另一個Oracle數據庫中。
(5)PL/SQL程序減少了網絡的交互,有助於提高程序性能。
PL/SQL引擎用來編譯和執行PL/SQL塊或子程序,該引擎駐留在Oracle服務器中,僅執行過程語句,面將SQL語句發送給Oracle服務器上的SQL語句執行器。
PL/SQL是一種塊結構的語言,它將一組語句放在一個塊中。匿名塊是一個未在數據庫中命名的PL/SQL塊,在運行時被傳遞到PL/SQL引擎以便執行。在PL/SQL塊中可以執行DML語句、TCL語句、SQL函數等。但PL/SQL塊中不可以執行
DDL語句,該種語句只能由動態SQL來執行。
PL/SQL塊由3部分組成:聲明部分、執行部分、異常處理部分
[declare]
--聲明部分:在此聲明PL/SQL用到的變量、類型及遊標,以及局部的存儲過程和函數
begin
--執行部分:過程及SQL語句,即程序的主要部分,不可省略
[exception]
--異常處理部分:錯誤處理
end;
關系運算符
運算符 |
意義 |
= |
等於 |
<>、!=、~=、^= |
不等於 |
< |
小於 |
> |
大於 |
<= |
小於等於 |
>= |
大於等於 |
一般運算符
運算符 |
意義 |
+ |
加號 |
- |
減號 |
* |
乘號 |
/ |
除號 |
:= |
賦值符號 |
=> |
關系符號 |
.. |
範圍運算符號 |
|| |
字符連接符號 |
邏輯運算符
運算符 |
意義 |
is null |
是空值 |
between and |
介於兩者之間 |
in |
在一列值中間 |
and |
邏輯與 |
or |
邏輯或 |
not |
取反,如is not null,not in |
變量和常量的聲明:
變量:variable_name data_type[(size)][:= init_value];
--variable_name表示變量名稱
--data_type表示變量的SQL或PL/SQL數據類型
--size指定變量的範圍。
--init_value指定變量的初始值。
在聲明變量時,可以用彈框的方式,給變量賦值。如:v_ename varchar2(10) :=&請輸入名字;
--這裏的” & ”符號就是關鍵字,後面的“當輸入名字”是提示信息。默認值輸入框中輸入的是數字,如果要輸入字符
串,則需要給要輸入的數據加上雙“’”單引號。
常量:variable_name CONSTANT data_type := value;
例,給常量和變量聲明賦值:
declare
v_ename varchar2(20);
v_no number;
v_rate number(7,2);
c_rate_incr constant number(7,2) := 1.10;
begin
--方法一:通過select into 給變量賦值,查詢結果只返回一條數據並賦值到變量中保存,返回多條或零條數據則報錯。
select ename,sal*c_rate_incr into v_ename, v_rate
from employee
--方法二:通過賦值操作符“:=”給變量賦值,在賦值時可以使用序列
v_ename := ‘SOCTT’;
v_no := myseq.nextval;
end;
PL/SQL中標識符定義的要求和限制:
1)標識符名稱不能超過30個字符。
2)第一個字符必須是字母。
3)不區分大小寫。
4)不能用”-”減號。
5)不能用SQL的保留字。
命名方法:
標識符 |
命名規則 |
例子 |
程序變量 |
v_name |
v_student_name |
程序常量 |
c_name |
c_company_name |
遊標變量 |
cursor_name |
cursor_emp |
異常標誌 |
e_name |
e_too_many |
表類型 |
name_table_type |
emp_record_type |
表 |
name_table |
emp_table |
記錄類型 |
name_record |
emp_record |
綁定變量 |
g_name |
g_year_sal |
PL/SQL中編碼規則:
(1)利用縮進排列展現邏輯結構。保留字後出現的列開始縮進三個空格,如declare下面聲明變量行。
(2)利用大小寫增強可讀性。保留字統一大寫; 應用程序專用名稱或標識符統一小寫。
(3)格式化單獨語句。每行至多寫一條語句; 聲明語句中盡量保持聲明的數據類型近變量名,而不是與數據類型對齊。
(4)格式化SQL語句。右對齊DML語句的子句中的保留字。
註釋
1)使用雙“-”減號加註釋,只在一行有效,如:
v_sal NUMBER(12,2); --人員的工資變量
2)使用“/* */”來加一行或多行註釋,建議使用如下方式
/*
||在註釋首行只放斜線星號,標誌註釋開始
||,然後註釋塊每一行以雙垂直線開頭,突出後面的註釋內容。可以不寫,但為了可讀性
*/
PL/SQL數據類型
1.標量數據類型,包含單個值,沒有內部組件。包括數字、字符、布爾值和日期時間值。
類型 |
子類 |
說明 |
範圍 |
CHAR |
Character Nchar |
定長字符串 民族語言字符集 |
0~32767 可選,默認為1 |
VARCHAR2 |
Varchar String NVARCHAR2 |
可變字符串 民族語言字符集 |
0~32767 |
BINARY_INTEGER |
|
帶符號整數,為整數計算優化性能 |
|
NUMBER(p,s) |
Dec Double Precision Integer Int Numeric Real Small int |
小數,NUMBER的子類型 高精度實數 整數,NUMBER的子類型 整數,NUMBER的子類型 與NUMBER等價 與NUMBER等價 整數,比Integer小 |
|
LONG |
|
可變長度字符串 |
0~2147483647 |
DATE |
|
日期型 |
公元前4712年1月1日至 公元後4712年12月31日 |
BOOLEAN |
|
布爾型 |
TRUE,FALSE,NULL |
2.LOB(Large OBject)數據類型,用於存儲大的數據對象的類型,主要支持BFILE、BLOB、CLOB、NCLOB類型。
3.屬性類型,屬性用於引用變量或數據庫列的數據類型,以及表示表中一行的記錄類型。
1)%TYPE
定義一個變量,其數據類型與已經定義的某個數據變量(尤其是表的一列)的數據類型一致。
2)%ROWTYPE
返回一個記錄類型,其數據類型和數據庫表的數據結構一致。
例,根據員工編號查詢員工信息:
DECLARE
v_empno employee.empno%TYPE := 7788;
v_rec employee%ROWTYPE;
BEGIN
SELECT * INTO v_rec FROM employee WHERE empno=v_empno;
DBMS_OUTPUT.PUT_LINE(‘姓名:’||v_rec.ename||’工資:’||v_rec.sal);--輸出一行,只在PL/SQL塊中
END;
PL/SQL控制語句
1.條件控制,用於根據條件執行一系列語句。條件控制包括IF語句和CASE語句。
IF語句語法:
IF <布爾表達式> THEN --if
--PL/SQL和SQL語句
END IF;
-------------
IF <布爾表達式> THEN --if-else
--PL/SQL和SQL語句
ELSE
--其他語句
END IF;
-------------
IF <布爾表達式> THEN --if-else if-else
--PL/SQL和SQL語句
ELSIF <其他表達式> THEN -- 這裏是elsif 而不是elseif,註意!!!
--其他語句
ELSIF <其他表達式> THEN
--其他語句
ELSE
--其他語句
END IF;
CASE語句語法:
--格式一
CASE 條件表達式
WHEN 條件表達式結果1 THEN 語句段1
WHEN 條件表達式結果2 THEN 語句段2
.....
WHEN 條件表達式結果n THEN 語句段n
[ELSE 語句段]
END CASE;
--格式二
CASE
WHEN 條件表達式1 THEN 語句段1
WHEN 條件表達式2 THEN 語句段2
.....
WHEN 條件表達式n THEN 語句段n
[ELSE 語句段]
END CASE;
2.循環控制,用於重復執行的系列語句。包括LOOP和EXIT語句,使用EXIT語句可以立即退出循環; 使用EXIT WHEN
語句可以根據條件結束循環。有3種類型循環,包括LOOP循環、WHILE循環、FOR循環。
LOOP循環語法:
LOOP
要執行的語句;
EXIT WHEN <條件語句> --條件滿足,退出循環語句
END LOOP;
WHILE循環語法:
WHILE <布爾表達式> LOOP
要執行的語句;
END LOOP;
FOR循環語法:
FOR 循環計數器 IN [REVERSE] 下限 .... 上限 LOOP
要執行的語句;
END LOOP;
3.順序控制,用於按順序執行語句。順序控制包括NULL語句和GOTO語句。GOTO語句不推薦使。
NULL語句:是一個可執行語句,相當於一個占位符或不執行任何操作的空語句,可以便某些語句變得有意義,提高程
序的可讀性,保證其他語句結構的完整性和正確性。
例,顯示變量v_counter的值,如果該變量小於10,則增加10並顯示該變量改變後的值。
DECLARE
v_counter NUMBER := 5;
BEGIN
DBMS_OUTPUT.PUT.LINE(‘v_counter的當前值為:’||v_counter);
IF v_counter>=10 THEN
NULL; --為了使語法變得有意義,去掉NULL會報語法錯誤
ELSE
v_counter := v_counter+10;
DBMS_OUTPUT.PUT.LINE(‘v_counter的改變後值為:’||v_counter);
END IF;
END;
異常處理
在程序運行時出現的錯誤叫異常。發生異常後,語句將停止執行,PL/SQL引擎立即將控制權轉到PL/SQL塊的異常處理。
PL/SQL預定義異常
異常 |
說明 |
ACCESS_INTO_NULL |
在未初始化對象時出現 |
CASE_NOT_FOUND |
CASE語句中的選項與用戶輸入的數據不匹配時出現 |
COLLECTION_IS_NULL |
給尚未初始化的表或數組賦值時出現 |
CORSOR_ALREADY_OPEN |
在用戶試圖重新打開已經打開的遊標時出現。在重新打開遊標前必須先將其關閉 |
DUP_VAL_ON_INDEX |
在用戶試圖將重復的值存儲在使用唯一索引的數據庫列中時出現 |
INVALID_CURSOR |
在執行非法遊標運算(如打開一個尚未打開的遊標)時出現 |
INVALID_NUMBER |
在將字符串轉換為數字時出現 |
LOGIN_DENIED |
在輸入的用戶名或密碼無效時出現 |
NO_DATA_FOUND |
在表中不存在請求的行時出現。此外,當程序引用已經刪除的元素時,也會引發NO_DATA_FOUND異常 |
STORAGE_ERROR |
在內存損環或PL/SQL耗盡內存時出現 |
TOO_MANY_ROWS |
在執行SELECT INTO語句後返回多行時出現 |
VALUE_ERROR |
產生大小限制錯誤時出現。例如,變量中的列值超出變量的大小 |
ZERO_DIVIDE |
以零作為除數時出現 |
異常處理語法:
BEGIN
sequence_of_statements;
EXCEPTION
WHEN <exception_name1> THEN
sequence_of_statements;
WHEN <exception_name2> THEN
sequence_of_statements;
WHEN OTHERS THEN --這裏的OTHERS處理程序除之前異常類型外的所有異常。PL/SQL塊只能有一個OTHERS
sequence_of_statements;
END;
--可以使用函數SQLCODE和SQLERRM來返回錯誤代碼和錯誤文本信息。
用戶自定義異常:
步聚如下
(1)在PL/SQL塊的定義部分定義異常情況:
<異常情況> EXCEPTION;
(2)拋出異常情況:
RAISE <異常情況>;
(3)在PL/SQL塊的異常情況處理部分對異常情況做出相應的處理。
例,查詢編號為7788的雇員的福利補助comm列。
DECLARE
v_comm employee.comm%TYPE;
e_comm_is_null EXCEPTION; --定義異常類型變量
BEGIN
SELECT comm INTO v_comm FROM employee WHERE empno=7788;
IF v_comm IS NULL THEN
RAISE e_comm_is_null;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE(‘雇員不存在!錯誤為:’||SQLCODE||SQLERRM);
WHEN E_COM_IS_NULL THEN
DBMS_OUTPUT.PUT_LINE(‘該雇員無補助!’);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(‘出現其他異常’);
END;
RAISE_APPLICATION_ERROR存儲過程可以重新定義異常錯誤消息,為應用程序提供一種與Oracle交互的方法。
語法:
RAISE_APPLICATION_ERROR(error_number, error_message);
--error_number表示用戶為指定的編號。該編號必須是-20999~-20000之間的負整數。
--error_message表示用戶為異常指定的消息文本。消息長度可達2048字節,是與error_number相關的。
例:
DECLARE
......
BEGIN
......
IF v_com IS NULL THEN
RAISE_APPLICATION_ERROR(-20001,’該雇員無補助’);
END IF;
END;
顯示遊標
Oracle會在內在中分配一個緩沖區,將執行結果放在這個緩沖區,而遊標是指向該區的一個指針。遊標為應用程序提供了一種對多行數據查詢結果集中的每一行數據分別進行單獨處理的方法,是設計嵌入式SQL語句的應用程序的常用編程式。
分類:
1)靜態遊標,是在編譯時知道明確的SELECT語句的遊標。靜態遊標分為隱式遊標、顯式遊標。
2)動態遊標
顯式遊標的使用步聚:
1)聲明遊標
CURSOR cursor_name [(parameter [,paramter]....)]
[RETURN return_type] IS select_statements;
--cursor_name指遊標的名稱。
--parameter用於為遊標指定輸入參數。在指定數據類型時,不能使用長度約束。
--return_type用於定義遊標提取的行的類型。
--select_statement 指遊標定義的查詢語句。
2)打開遊標
OPEN cursor_name[ (parameters) ];
3)提取遊標
FETCH cursor_name INTO variables;
--variables是變量名
4)關閉遊標
CLOSE cursor_name;
例:
DECLARE name employee.ename%TYPE; sal employee.sal%TYPE; --定義兩個變量來存放ename和sal的內容 CURSOR emp_cursor --聲明遊標 IS SELECT ename,sal FROM employee; BEGIN OPEN emp_cursor; LOOP FETCH emp_cursor INTO name, sal; EXIT WHEN emp_cursor%NOTFOUND; DBMS_OUTPUT.PUT_LINE(‘第’||emp_cursor%ROWCOUNT||’個雇員:’||name||sal); END LOOP; CLOSE emp_cursor; END;
顯式遊標屬性
1)%FOUND:只有在DML語句影響一行或多行時,%FOUND屬性才返回TRUE;
2)%NOTFOUND:%NOTFOUND屬性與%FOUND屬性的作用正好相反。如果DML語句沒有影響任何行,則%NOTFOUND屬性返回TRUE;
3)%ROWCOUNT:%ROWCOUNT屬性返回DML語句影響的行數。如果DML語句沒有影響任何行,則%ROWCOUNT屬性將返回0;
4)%ISOPEN:%ISOPEN屬性返回遊標是否已打開。
使用顯式遊標刪除或更新
使用遊標時,如果處理過程中需要刪除或更新行,在定義遊標時必須使用SELECT....FOR UPDATE語句; 而在執行DELETE和UPDATE時使用WHERE CURRENT OF子句指定遊標的當前行。
聲明更新遊標語法:
CURSOR cursor_name IS
select_statement FOR UPDATE [OF culumns];
--SELECT... FOR UPDATE[OF columns]為更新查詢,鎖定選擇的行。[OF columns]可以不是SELECT後的列
--(1)當選擇單表更新查詢時,可以受省略OF子句;
--(2)當選擇多個表更新查詢時,被鎖定的行來源於OF 子句後聲明的列所在的表中的行。
更新執行行語法:
UPDATE table_name
SET column_name= column_value
WHERE CURRENT OF cursor_name;
--多表查詢更新時,更新表為鎖定列所在的表。
例:
DECLARE CURSOR emp_cursor IS SELECT ename, sal FROM employee e INNER JOIN dept d ON e.deptno= d.deptno FOR UPDATE OF SAL; .... UPDATE employee SET sal=sal+100 WHERE CURRENT OF emp_cursor;
使用循環遊標簡化遊標的讀取,循環遊標隱式打開遊標,自動從活動集獲取行,在處理實所有行時自動關閉遊標。
語法:
FOR record_index IN cursor_name LOOP executable_statements END LOOP;
--record_index是PL/SQL聲明的記錄變量。此變量的屬性聲明為%ROWTYPE類型作用域在循環之內。
循環遊標的特性:
1)在遊標中提取了所有記錄之後自動終止。
2)提取和處理遊標中的每一條記錄。
3)如果在提取記錄之後%NOTFOUND屬性返回TRUE,則終止循環。
4)如果未返回行,則不進入循環。
例,顯示雇員表中所有雇員的姓名和薪水:
DECLARE CURSOR emp_cursor IS SELECT ename, sal FROM employee; BEGIN FOR emp_record IN emp_cursor LOOP DBMS_OUTPUT.PUT_LINE(‘第’||emp_cursor%ROWCOUNT||’個雇員:’||emp_record.ename|| emp_record.sal); END LOOP; END;
NOT_DATA_FOUND和%NOTFOUND的區別
1)SELECT ··· INTO語句返回0條和多條記錄時觸發NO_DATA_FOUND。
2)當UPDATE或DELETE語句的WHERE 子句未找到時,觸發%NOTFOUND。
3)在提取循環中用%NOTFOUND或%FOUND來確定循環的退出條件,而不用NO_DATA_FOUND。
存儲過程
子程序
子程序是已命名的PL/SQL塊,它們存儲在數據庫中,包括存儲過程和函數。使用存儲過程執行操作,使用函數執行操作並返回值。
1.聲明部分,包括類型、遊標、常量、異常和嵌套子程序的使用。退出子程序後雩不復存在。
2.可執行部分,包括賦值、控制執行過程以及操縱Oracle數據的語句。
3.異常處理部分,包括異常處理程序,負責處理執行存儲過程中出現的異常。
子程序的優點:
1)模塊化:通過子種序,可以將程序分解為可管理的、明確的邏輯模塊。
2)可重用性:子程序在創建並執行後,就可以在任意數目的應用程序中使用。
3)可維護性:子程序可以簡化維護操作,因為如果一個子程序受到影響,則只需要修改該子程序的定義。
4)安全性:用戶可以設置權限,使得訪問數據的唯一方式就是用戶提供的存儲過程。這樣也可以保證正確性。
存儲過程:
存儲過程是執行某些程序操作的子程序,是執行特定任務的模塊。從根本上來講,存儲過程就是命名後的PL/SQL塊。
創建存儲過程:
CREATE [OR REPLACE] PROCEDURE procedure_name [(parameter_list)] [IS|AS] [local_declarations] BEGIN execetable_statements [EXCEPTION] [exception_handlers] END [procedure_name];
--procedure_name:存儲過程的名稱。
--parameter_list:參數列表,可選。
--local_declarations:局部聲明,可選。
--executable_statements:可執行語句。
--excetption_handlers:異常處理程序,可選。
--OR REPLACE:可選。如果包含OR REPLACE語句,當系統中有這個存儲過程時,將覆蓋。如果不包含,當系統中有這個存儲過程時,將報異常。
--聲明時,數據類型不能帶大小。如NUMBER(4),只能寫成NUMBER。
--聲明時可以給定默認值,如:job VARCHAR2 DEFAULT ‘CLEAK’ 如果調用時不揭定該參數,自動默認值。
調用存儲過程(存儲過程通過授權,才可以調用)
1.用命令調用
1)執行:
EXEC[UTE] procedure_name (parameter_list);
--EXECUTE:執行命令,可以縮寫為EXEC
2)參數的傳遞方式
按位置傳遞,例:EXEC add_employee(1111,’MARY’,2000,’MANAGER’,10);
按名稱傳遞,即在調用時按名稱對應。名稱的對應關系是最重要的,次序不重要。
例:EXEC add_employee(dno=>10,name=>’MARY’,salary=>2000,job=>’MANAGER’);
混合方法傳遞,
例:EXEC add_employee(1113,dno=>10,name=>’MARY’,salary=>200,job=>’MANAGER);
其中雇員編號為1113,後面如果有一個是按照名稱傳遞,則之後的都要以名稱傳遞。
2.PL/SQL塊中使用
BEGIN add_employee(2111,’MARY’,2000,’MANAGER’,10); END;
--在PL/SQL塊中調用存儲過程時不需要寫EXEC,直接寫存儲過程名稱即可,EXEC是命令行中調用存儲過程
的命令。
3.存儲過程的參數模式
在存儲過程中參數傳遞的模式有3種:IN、OUT、IN OUT,即輸入 、輸出、輸入/輸出參數。
IN模式只能將實參傳遞給形參,進入函數內部,函數返回時,實參值不變。結論參數傳遞 模式。
OUT模式會忽略調用時的實參值,在函數內部可寫,函數返回時實參的值改變。
IN OUT模式具有前再種模式的特性。即調用時,實參的值總是傳遞給形參;結束時,形參的值傳遞給實參
可以在參數列表中為IN參數賦默認值。但是OUT和INT OUT參數不可以賦默認值。
如:
CREATE OR REPLACE PROCEDURE QueryEmp ( v_empno IN employee.empno%TYPE DEFAULT 1000, --默認編號為1000 v_ename OUT employee.ename%TYPE, v_sal IN OUT employee.sal%TYPE ) AS ......
4.存儲過程的訪問權限。
如果非創建該存儲過程的用戶想要訪問該存儲過程,則需要得到存儲過程的EXECUTE權限。
--授權
GRANT EXECUTE ON add_employee TO A_oe;
--撤銷
REVOKE EXECUTE ON add_employee FROM A_oe;
5.刪除存儲過程
DROP PROCEDURE procedure_name;
存儲過程的調試與跟蹤
1.在SQL*Plus下調試
如: SQL>SET SERVEROUTPUT ON;
SQL>--調用add_employee存儲過程
SQL>SHOW ERRORS PROCEDURE add_employee;
2.用PL/SQL Developer工具調試
1)獲得DEBUG CONNECT SESSION權限
GRANT DEBUG CONNECT SESSION TO A_hr;
2)打開一個測試窗口,在裏面編寫存儲過程代碼。
3)按“F9”鍵進入調試狀態。或者測試窗口左上角的黃色齒輪按鈕。
4)在開始調試按鈕右邊第二個按鈕是單步進入按鈕。
存儲過程規範
1.存儲過程不可以直接使用DDL語句。可以通手動態SQL實現,最好不要使用。
2.存儲過程必須有相應的出錯處理功能。
3.存儲過程中變量在引用表字段的時候,需使用%TYPE和%ROWTYPE類型。
4.存儲過程必須包含兩個輸出參數,即on_Flag(number)和 os_Msg(varchar2),分別用於標識過程的執行狀態及過程提示信息。
其中on_Flag有三種取值情況:
0 表示過程執行成功但無提示信息;
大於0 表示過程執行成功但有提示信息;
小於1 表示過程執行失敗且有提示信息;
5.必須在存儲過程中做異常捕獲,並將異常信息通過os_Msg變量輸出。
6.-1999~-1的異常為Oracle定義的異常代碼。
7.“WHEN OTHERS”必須放貫在異常處理代碼的最後作為默認處理器處理沒有顯式處理的異常。
例:
CREATE OR REPLACE PROCEDURE add_employee( .... on_Flag OUT NUMBER, --執行狀態 os_Msg OUT VARCHAR2 ) IS BEGIN INSERT INTO ..... on_Flag:=1; os_Msg:=’添加成功’; EXCEPTION WHEN DUP_VAL_ON_INDEX THEN on_Flag=-1; os_Msg=’該雇員已存在’; WHEN OTHERS THEN on_Flag=-2; os_Msg=’其他錯誤,與管理員聯系。’; END;
Oracle之PL/SQL編程