1、基礎知識
PLSQL是一種類Pascal語言,每一段程序都是由Block(代碼塊)組成
declare
變量定
begin
sql語句
pl語句
exception
異常處理
end;
PLSQL塊分為三種:
- 匿名塊 (Anonymous)
- 存儲過程 (Procedure)
- 函數 (function)
上面舉得例子就是匿名塊,因為沒有名字,所以叫匿名塊。存儲過程和函數都是有名字的,函數有返回值。
2、PLSQL變量
PLSQL變量主要有以下四種:
- 系統內置的常規簡單變量類型
- 用戶自定義復雜類型變量
- 引用類型變量(保存了一個指針值)
- 大對象類型(LOB)
變量類型舉例:
- 布爾類型
- 日期類型
- BFILE的二進制文件類型
- 日期類型
- BLOB類型
- long類型,長字符串
- 字符串類型
變量聲明舉例:
DECLARE v_hiredateDATE;
v_deptno NUMBER(2) NOT NULL := 10;
v_location VARCHAR2(13) := 'Atlanta';
c_comm CONSTANT NUMBER := 1400;
- 變量聲明后再沒有賦值之前其值為NULL
- 同一個塊中,應該避免多個變量使用相同的名字
常規變量的申明:
DECLARE
v_job VARCHAR2(9);
v_count BINARY_INTEGER := 0;
v_total_sal NUMBER(9,2) := 0;
v_orderdate DATE := SYSDATE + 7;
c_tax_rate CONSTANT NUMBER(3,2) := 8.25;
v_valid BOOLEAN NOT NULL := TRUE;
...
-- 特殊申明變量方式:
v_name employees.last_name%TYPE;
v_min_balance v_balance%TYPE := 10;
begin
null;
end;
PLSQL中的游標
游標概論:
游標是一個私有的SQL工作區域,Oracle數據庫中有兩種游標,分別是隱式游標和顯式游標,隱式游標不易被用戶和程序員察覺和意識到,實際上Oracle服務器使用隱式游標來解析和執行我們提交的SQL語句; 而顯式游標是程序員在程序中顯式聲明的;通常我們說的游標均指顯式游標。
顯示游標
對于返回多行結果的SQL語句的返回結果,可使用顯式游標獨立的處理器中每一行的數據。
此處圖片缺失
顯式游標控制的一般過程:
DECLARE
v_empno employees.employee_id%TYPE;
v_ename employees.last_name%TYPE;
CURSOR emp_cursor IS --1.創建游標
SELECT employee_id, last_name FROM employees;
BEGIN
OPEN emp_cursor; --2.打開游標
LOOP
FETCH emp_cursor
INTO v_empno, v_ename; --3.提取變量 fetch cursor_name into value1,value2;
EXIT WHEN emp_cursor%ROWCOUNT > 10 OR emp_cursor%NOTFOUND; --emp_cursor%NOTFOUND 4.用來測試是否有數據
DBMS_OUTPUT.PUT_LINE(TO_CHAR(v_empno) || ' ' || v_ename);
END LOOP;
CLOSE emp_cursor; --5.關閉游標
END;
for
循環控制游標
舉例1:直接使用for+sql
創建并適用游標
--一般格式:
--for record_name in cursor_name loop ... end loop;
BEGIN
FOR emp_record IN (SELECT last_name, department_id
FROM employees) LOOP
-- implicit open and implicit fetch occur
IF emp_record.department_id = 80 THEN
...
END LOOP; -- implicit close occurs
END;
舉例2:先創建游標emp_cursor
,再用for調用
--這種情況適用于多次調用游標的情況
DECLARE
CURSOR emp_cursor IS --先創建游標
SELECT last_name, department_id
FROM employees;
BEGIN
FOR emp_record IN emp_cursor LOOP --再調用游標訪問
-- implicit open and implicit fetch occur
IF emp_record.department_id = 80 THEN
...
END LOOP; -- implicit close occurs
END;
游標帶參數
直接返回和參數相關的查詢結果
DECLARE
CURSOR emp_cursor
(p_deptno NUMBER, p_job VARCHAR2) IS
SELECT employee_id, last_name
FROM employees
WHERE department_id = p_deptno
AND job_id = p_job;
BEGIN
OPEN emp_cursor (80, 'SA_REP');
. . .
CLOSE emp_cursor;
OPEN emp_cursor (60, 'IT_PROG');
. . .
END;
在游標中使用FOR UPDATE NOWAIT
有的時候我們打開一個游標是為了更新或者刪除一些記錄,這種情況下我們希望
在打開游標的時候即鎖定相關記錄,應該使用for update nowait
語句,倘若鎖定失敗我們就停止不再繼續,以免出現長時間等待資源的死鎖情況。
舉例:
DECLARE
CURSOR emp_cursor IS
SELECT employee_id, last_name, department_name
FROM employees,departments
WHERE employees.department_id =
departments.department_id
AND employees.department_id = 80
FOR UPDATE OF salary NOWAIT;
PLSQL中的異常
PLSQL中一般有兩種異常,一種是由Oracle內部錯誤拋出的異常,分為預定義和非預定義兩種;另外一種是由程序員顯式的拋出。
常見的異常:
異常代碼 | 異常類型 |
---|---|
NO_DATA_FOUND | 沒有發現數據 |
INVALID_CURSOR | 游標在被打開以前,引用%NOTFOUND屬性會產生此異常 |
TOO_MANY_ROWS | 返回數據過多 |
ZERO_DIVIDE | 除數為零,出現異常 |
DUP_VAL_ON_INDEX | 唯一索引上有重復值 |
對othres的處理
others表明我們未能預計的錯誤,所以全部歸到othres中去,單發生這種情況時,我們希望了解錯誤號和相關信息,可以使用Oracle內置的函數SQLCODE和SQLERRM來返回錯誤號和錯誤描述,舉例見下:
DECLARE
v_error_code NUMBER;
v_error_message VARCHAR2(255);
BEGIN
...
EXCEPTION
...
WHEN OTHERS THEN
ROLLBACK;
v_error_code := SQLCODE ;
v_error_message := SQLERRM ;
INSERT INTO errors
VALUES(v_error_code, v_error_message);
END;
處理用戶自定義異常
此處圖片缺失
舉例:
DECLARE
e_invalid_department EXCEPTION;
BEGIN
UPDATE departments
SET department_name = &p_department_desc
WHERE department_id = &p_department_number;
IF SQL%NOTFOUND THEN
RAISE e_invalid_department;
END IF;
COMMIT;
EXCEPTION
WHEN e_invalid_department THEN
DBMS_OUTPUT.PUT_LINE('No such department id.');
END;
傳遞異常到外層代碼塊
DECLARE
. . .
e_no_rows exception;
e_integrity exception;
PRAGMA EXCEPTION_INIT (e_integrity, -2292);
BEGIN
FOR c_record IN emp_cursor LOOP
--內層代碼塊
BEGIN
SELECT ...
UPDATE ...
IF SQL%NOTFOUND THEN
RAISE e_no_rows; --將異常扔到外層代碼塊中,使用關鍵字:RAISE
END IF;
END;
END LOOP;
EXCEPTION
WHEN e_integrity THEN ...
WHEN e_no_rows THEN ...
END;
存儲過程
存儲過程基本語法
語法:
CREATE [OR REPLACE] PROCEDURE procedure_name
[(parameter1 [mode1] datatype1,
parameter2 [mode2] datatype2,
. . .)]
IS|AS
PL/SQL Block;
舉例:
CREATE OR REPLACE PROCEDURE raise_salary
(p_id IN employees.employee_id%TYPE)
IS
BEGIN
UPDATE employees
SET salary = salary * 1.10
WHERE employee_id = p_id;
END raise_salary;
存儲過程參數模式
IN | OUT | IN OUT |
---|---|---|
默認模式 | 必須顯示指定 | 必須顯示指定 |
用以把值傳給過程 | 用以把值從過程返回給調用環境 | 用以把變量傳遞給過程,并返回給調用環境 |
參數可以是常數、變量、表 | 必須是個變量 | 必須是個變量 |
必須是個變量 | 不能賦予默認值 | 不能賦予默認值 |
舉例:IN OUT型變量的應用
--程序功能為:將傳入的電話號碼字符串按xxx-xxx-xxxxx的形式返回
CREATE OR REPLACE PROCEDURE format_phone
(p_phone_no IN OUT VARCHAR2)
IS
BEGIN
p_phone_no := '(' || SUBSTR(p_phone_no,1,3) ||
')' || SUBSTR(p_phone_no,4,3) ||
'-' || SUBSTR(p_phone_no,7);
END format_phone;
/
存儲過程的參數傳遞
- 使用默認值
- 按順序傳遞
- 使用
=>
傳遞
舉例:
首先創建存儲過程add_dept
,設定了兩個參數,p_name
和p_loc
--設定默認參數使用關鍵字:DEFAULT
CREATE OR REPLACE PROCEDURE add_dept
(p_name IN departments.department_name%TYPE DEFAULT 'unknown',
p_loc IN departments.location_id%TYPE DEFAULT 1700)
IS
BEGIN
INSERT INTO departments(department_id,
department_name, location_id)
VALUES (departments_seq.NEXTVAL, p_name, p_loc);
END add_dept;
/
然后,使用調用add_dept
,分別使用三種方式傳遞參數
BEGIN
add_dept; --1.默認參數
add_dept ('TRAINING', 2500); --2.順序傳遞
add_dept ( p_loc => 2400, p_name =>'EDUCATION');--3.使用=>傳遞
add_dept ( p_loc => 1200) ;
END;
/
SELECT department_id, department_name, location_id
FROM departments; --sql查詢驗證
存儲過程處理異常
此處圖片缺失
函數
自治事務
關鍵字:
一個事務A在另一個事務B內被調用,那個事務A是自治事務,自治事務A執行過程中會脫離其session內未執行完畢的事務的影響。如果session從B事務開始——A事務開始和結束——B事務結束,上述的A事務不受沒有完成的B事務的影響,然后A事務執行完畢后再次回到B事務執行沒有完成的B事務。
創建自治事務語法:
Create or replace procedure pro_name as pragma AUTONOMOUS_TRANSACTION;
Begin
null;
End;
下面通過一個例子來對自治事務進行說明和解釋,首先創建一個procedure—noautonomous_transaction01
:
Create or replace procedure noautonomous_transaction01 as
Begin
Insert into test01 select * from test01 where rownum=1;
Commit;
End;
--進行查詢測試
Select count(*) from test01
--結果:Count(*)|25
此時,發現test01有25條數據
declare
begin
Insert into test01 select * from test01 where rownum=1;
Noautonomous_transaction01;--調用事務
Rollback;
End;
上述pl/sql程序塊內嵌套了一個dml的procedure
過程。執行pl/sql程序塊結束后發現test01有27條數據,也就說明了procedure
中的commit
完成了上述procedure
和pl/sql程序塊的提交。最后的rollback
也就沒有任何效果。
結論:非自治事務內的procedure中得commit或者rollback會影響前面所有dml的影響。
現在創建一個自治事務:
--Create Procedure的autonomous_transaction01
Create or replace procedure autonomous_transaction01 as pragma autonomous_transaction01;
Begin
Insert into test01select * from test01 where rownum=1;
Commit;
End;
--查詢進行測試
Select count(*) from test01
--Count(*)|25
此時test01有25條數據。然后,在pl/sql程序塊中調用自治事務autonomous_transaction01
:
Begin
Insert into test01 select * from test01 where rownum=1;
Autonomous_transaction01;
Rollback;--rollback并沒有影響到自治事務內的操作
End;
自治事務內的commit
只會影響自治事務內的沒有提交的dml,調用自治事務完畢后又會回到調用自治事務的事務內,此時最后的rollback
也只能回滾自治事務外的dml,所以此時的test01中還是只有25條數據。
自治事務與被調用事務完全獨立,不能共享調用者事務使用的鎖和其他資源,而且自治事務內還可以調用其他自治事務。自治事務與其調用者可能會發生死鎖,oracle會檢測此類死鎖并返回錯誤信息。
文件操作
系統函數:
utl_file.fopen(三個參數:文件對象,文件名,打開方式) 打開文件
utl_file.put_line(文件對象,字符串) 輸出文件
utl_file.get_line() 讀取文件
utl_file.fclose() 關閉文件
utl_file.fflush() 強制輸出緩沖
文件打開方式
也就是fopen()的第三個參數:
r -- read text
w -- write text
a -- append text
rb -- read byte mode
wb -- write byte mode
ab -- append byte mode
--如果指定'a'或者'ab'但是文件不存在會先創建。
舉例:
CREATE OR REPLACE PROCEDURE FILE_TEMP IS
FILEHANDLE UTL_FILE.FILE_TYPE;--定義文件對象
BEGIN
--打開文件
FILEHANDLE := UTL_FILE.fopen('FILENAME' ,'FILE_TEMP.TXT' ,'W' );
--寫入文件
UTL_FILE.put_line(FILEHANDLE,'這是一個練習和測試');
FOR TEMP_1 IN (SELECT * FROM EMPLOYEES) LOOP
--寫入文件
UTL_FILE.put_line(FILEHANDLE,TEMP_1.LAST_NAME || '|' || TEMP_1.SALARY);
END LOOP;
UTL_FILE.FCLOSE(FILEHANDLE);--關閉文件
END;
引用游標
create or replace procedure test_ref1
is
type s_table is ref cursor;
f s_table;
f_rec t1% ;
stmt varchar2(100) := 'select empno,ename from emp';
begin
insert into t1 select empno,ename,sal from emp;
open f for select * from t1;
loop
fetch f into f_rec;
dbms_output.put_line(f_rec.empno ||' '||f_rec.ename);
exit when f%rowcount=3 ;
end loop;
close f;
end;
/
上課記錄
-
begin ...end;
為一個程序塊
- 匿名塊不能被調用,因為沒有名字,一般用于測試,可以直接執行
- 存儲過程和函數相比,沒有返回值,兩者都需要編譯后進行調用,才可以執行。
- 返回值和參數定義不需要指定長度,定義變量需要指定長度