繼續上文的初級篇,聊一聊存儲過程的常用特性,談談存儲過程在工作中的具體使用,希望能對讀者有所幫助。
參數傳入模式
PL/SQL存儲過程有三種傳參模式in 、out、in out。默認情況下(即不寫)為in模式
先來看看下面的存儲過程:
--in 、out、 in out模式測試
create or replace procedure proc_param_demo(p1 in number,p2 out number,p3 in out number) as
begin
dbms_output.put_line('測試in 模式存儲過程中p1的值為:'||p1);--標准輸出
dbms_output.put_line('測試out 模式存儲過程中p2的值為:'||p2);
dbms_output.put_line('測試in out模式存儲過程中p3的值為:'||p3);
--p1 :=100; --報錯
p2 := 100; --:=PL/SQL標准賦值
p3 :=100;
end;
調試完錯誤之后,調用上面的存儲過程,這次調用我們不直接把值傳遞給存儲過程的參數。所以需要使用declare關鍵字來聲明變量,然后就可以在執行區域才可以使用這些變量:
declare --聲明區域
p1 number;
p2 number;
p3 number;
begin
p1 := 10;
p2 := 10;
p3 := 10;
proc_param_demo(p1,p2,p3);
dbms_output.put_line('測試in 模式存儲過程后p1的值為:'||p1);
dbms_output.put_line('測試out 模式存儲過程后p2的值為:'||p2);
dbms_output.put_line('測試in out模式存儲過程后p3的值為:'||p3);
end;
輸出結果如下圖所示:
通過測試可以得到以下結果:
- in模式可以理解為引用傳遞,它的值被完整的傳入到存儲過程中,但在執行過程中不能被重新賦值,我們傳遞的值執行之后不會發生改變。
- out模式為值傳遞,它的值在傳入到存儲過程的時候會默認賦值為null,可以在執行的時候為其賦值,執行之后傳遞的值也會隨之改變
- in out模式為值傳遞,它的值被完整的傳入到存儲過程中,可以在執行的時候為其賦值,執行之后傳遞的值也會隨之改變
總結:可以把in模式看做是向存儲過程傳遞的不想被改變的參數,可以把out模式看做返回值,當執行之后out模式的值就會隨着業務邏輯發生改變以供我們使用,而in out模式則很靈活,我們即可以把它當參數傳遞,也可以當做返回值來使用。
控制語句
流程控制語句語句無處不在,只要有一點編程基礎就能夠理解,在這里我列舉一些在工作中常用的語法,詳細語法請參考官方文檔
IF判斷語句
IF 條件 THEN
執行體;
END IF;
IF 條件 THEN
執行體;
ELSE
執行體;
END IF;
IF 條件 THEN
執行體;
ELSEIF 條件 THEN
執行體;
ELSE
執行體;
END IF;
CASE選擇語句
CASE 變量
WHEN 匹配變量 THEN 執行體;
WHEN 匹配變量 THEN 執行體;
WHEN 匹配變量 THEN 執行體;
ELSE 執行體;
END CASE;
LOOP循環語句
LOOP
執行體;
IF 條件 THEN
執行體;
EXIT;--此處為跳出循環
END IF;
END LOOP
LOOP
執行體;
EXIT WHEN 條件;--跳出循環 對比的有CONTINUE和CONTINUE WHEN語法
END LOOP
FOR i IN 1..3 LOOP -- FOR EACH 語法,遍歷1~3並輸出
--執行體;
DBMS_OUTPUT.PUT_LINE (TO_CHAR(i));
END LOOP;
流程控制語句先了解其語法即可,因為它無處不在,我們就可以在實踐中慢慢的掌握並精通。
Cursor游標
An explicit cursor names the unnamed work area in which the database stores processing information when it executes a multiple-row query. When you have named the work area, you can access its information, and process the rows of the query individually.
翻譯:Cursor就是當我們在數據庫一塊未命名的存儲數據的工作空間進行多行查詢的時候對其進行命名。當你對一塊工作空間命名之后,你就可以獲取並處理這些查詢的數據
總結:可以把Cursor理解為一個有名稱的結果集,當需要的時候就可以拿過來用,也可以吧Cursor看成一個實體類,存儲了我們定義的數據。
接下來我們通過看一個具體的例子了解cursor的具體實現,需求為從emp中查出所有的姓名和其職位輸出
create or replace procedure proc_cursor_demo as
cursor emp_cursor is --定義cursor
select * from emp; --將數據空間指向emp_cursor
begin
for emp_data in emp_cursor loop --遍歷cursor數據輸出
dbms_output.put_line(emp_data.ename ||' 的工作為: '||emp_data.job);
end loop;
end;
結果如下
異常
存儲過程的異常在工作中非常常用,但具體使用一般很簡單,包括自定義異常和系統異常,jojo52013145總結的很好,如果想深入了解請參考他的文章。
實踐
不管學習什么光說不練都是假把式,看到的知識不一定是自己的,只有實踐之后才能真正的理解,讓我們通過一個例子。比如說我們有這樣的一個需求,要把emp表中有獎金的員工提取到另外一個表,並在員工的姓名后添加后綴_V。下面就是實現代碼:
create or replace procedure proc_copy_demo
(tn in varchar2)
as
v_count number; --檢查是否已經有這個名稱的表了
tablename varchar2(20); --表名稱
v_emp_copy emp%rowtype; --表結構
v_sql varchar(200); --sql
cursor emp_data_cursor is -- 將有emp表中有獎金的人員放到cursor中
select * from emp where comm>0; --過濾有獎金的
begin
tablename := tn; -- 定義表名稱
select count(*) into v_count from user_objects where object_name = upper(tablename); -- 查詢用戶表,校驗是否已經存在名稱為tablename的表
if v_count>0 then -- 表存在
execute immediate 'drop table '|| tablename ||' cascade constraints'; -- 執行刪除表 execute immediate執行SQL語句
end if;
execute immediate 'create table '|| tablename ||' as select * from emp where 1=2'; -- 新建表結果和emp相同的表,添加條件后只創建表,不添加數據
for emp_data in emp_data_cursor loop -- 從cursor中取出數據,做一些處理並插入到指定表中
v_emp_copy.empno := emp_data.empno;
v_emp_copy.ename := emp_data.ename || '_A';
v_emp_copy.job := emp_data.job;
v_emp_copy.mgr := emp_data.mgr;
v_emp_copy.hiredate := emp_data.hiredate;
v_emp_copy.sal := emp_data.sal;
v_emp_copy.comm := emp_data.comm;
v_emp_copy.deptno := emp_data.deptno;
v_sql := 'insert into '||tablename||' values
(' ||v_emp_copy.empno ||','''
||v_emp_copy.ename ||''','''
||v_emp_copy.job ||''','
||v_emp_copy.mgr ||','''
||v_emp_copy.hiredate ||''','
||v_emp_copy.sal ||','
||v_emp_copy.comm ||','
||v_emp_copy.deptno ||')';
dbms_output.put_line(v_sql);
execute immediate v_sql;
end loop;
commit;
exception -- 捕獲異常常用寫法
when others then
dbms_output.put_line('捕獲的異常代碼(SQLCODE) 為 ' ||SQLCODE ); -- 輸出異常信息
dbms_output.put_line('捕獲的異常信息(SQLERRM) 為 ' ||SQLERRM);
end;
-------------------------test-------------------------------
begin
proc_copy_demo('emp_copy');
end;
-------------------------test-------------------------------
運行之后可能會出現下面的輸出,PL/SQL拋出了一個異常,通過異常捕獲機制,我們可以輕松的找到問題所在。
因為我們沒有為scott用戶賦予創建標的權限,具體代碼如下:
revoke create table from scott -- 收回scott創建表的權限
grant create table to scott -- 賦予scott創建表的權限
然后再執行上面的存儲過程,我們會發現想要的數據已經被提取到emp_copy表中了。