Oracle存储过程快速入门
1.什么是oracle存儲過程
存儲過程和函數也是一種PL/SQL塊,是存入數據庫的PL/SQL塊。但存儲過程和函數不同于已經介紹過的PL/SQL程序,我們通常把PL/SQL程序稱為無名塊,而存儲過程和函數是以命名的方式存儲于數據庫中的。和PL/SQL程序相比,存儲過程有很多優點,具體歸納:
1. 存儲過程和函數以命名的數據庫對象形式存儲于數據庫當中。存儲在數據庫中的優點是很明顯的,因為代碼不保存在本地,用戶可以在任何客戶機上登錄到數據庫,并調用或修改代碼。
2. 存儲過程和函數可由數據庫提供安全保證,要想使用存儲過程和函數,需要有存儲過程和函數的所有者的授權,只有被授權的用戶或創建者本身才能執行存儲過程或調用函數。
3. 存儲過程和函數的信息是寫入數據字典的,所以存儲過程可以看作是一個公用模塊,用戶編寫的PL/SQL程序或其他存儲過程都可以調用它(但存儲過程和函數不能調用PL/SQL程序)。一個重復使用的功能,可以設計成為存儲過程。
4. 像其他高級語言的過程和函數一樣,可以傳遞參數給存儲過程或函數,參數的傳遞也有多種方式。存儲過程可以有返回值,也可以沒有返回值,存儲過程的返回值必須通過參數帶回;函數有一定的數據類型,像其他的標準函數一樣,我們可以通過對函數名的調用返回函數值。
5. 存儲過程和函數需要進行編譯,以排除語法錯誤,只有編譯通過才能調用。
2.如何創建和刪除存儲過程
2.1.創建存儲過程的語法
CREATE [OR REPLACE] PROCEDURE 存儲過程名[(參數[IN|OUT|IN OUT] 數據類型...)]
{AS|IS}
[說明部分]
BEGIN
可執行部分
[EXCEPTION
錯誤處理部分]
END [過程名];
其中:
可選關鍵字OR REPLACE 表示如果存儲過程已經存在,則用新的存儲過程覆蓋,通常用于存儲過程的重建。
參數部分用于定義多個參數(如果沒有參數,就可以省略)。參數有三種形式:IN、OUT和IN OUT。如果沒有指明參數的形式,則默認為IN。 關鍵字AS也可以寫成IS,后跟過程的說明部分,可以在此定義過程的局部變量。(注意:大小寫不敏感)。
簡單的存儲過程示例:
create or replace procedure hello_world as
say_hi varchar2(20);
begin
say_hi := 'Hello World!';
dbms_output.put_line(say_hi);
end;
2.2 刪除存儲過程
語法:
DROP PROCEDURE 存儲過程名;
如:drop procedure hello_world;
3.存儲過程的查看
可以通過對數據字典的訪問來查詢存儲過程或函數的有關信息,如果要查詢當前用戶的存儲過程或函數的源代碼,可以通過對USER_SOURCE數據字典視圖的查詢得到。
3.1查看存儲過程的腳本
如查詢存儲過程hello_world的腳本
Select text from user_source where name = ‘HELLO_WORLD’;
(這里的過程名必須大寫)
3.2 查看存儲過程的狀態
如:select status from user_objects where object_name = ‘HELLO_WORLD’;
說明:VALID表示該存儲過程有效(即通過編譯),INVALID表示存儲過程無效或需要重新編譯。當Oracle調用一個無效的存儲過程或函數時,首先試圖對其進行編譯,如果編譯成功則將狀態置成VALID并執行,否則給出錯誤信息。
當一個存儲過程編譯成功,狀態變為VALID,會不會在某些情況下變成INVALID。結論是完全可能的。比如一個存儲過程中包含對表的查詢,如果表被修改或刪除,存儲過程就會變成無效INVALID。所以要注意存儲過程和函數對其他對象的依賴關系。
4.存儲過程參數說明
三種形式的參數
1. IN 定義一個輸入參數變量,用于傳遞參數給存儲過程
2. OUT 定義一個輸出參數變量,用于從存儲過程獲取數據
3. IN OUT 定義一個輸入、輸出參數變量,兼有以上兩者的功能
參數的定義形式和作用如下:
4.1 IN參數
語法:參數名 IN 數據類型 DEFAULT 值;
定義一個輸入參數變量,用于傳遞參數給存儲過程。在調用存儲過程時,主程序的實際參數可以是常量、有值變量或表達式等。DEFAULT 關鍵字為可選項,用來設定參數的默認值。如果在調用存儲過程時不指明參數,則參數變量取默認值。在存儲過程中,輸入變量接收主程序傳遞的值,但不能對其進行賦值。
4.2 OUT參數
語法:參數名 OUT 數據類型;
定義一個輸出參數變量,用于從存儲過程獲取數據,即變量從存儲過程中返回值給主程序。
在調用存儲過程時,主程序的實際參數只能是一個變量,而不能是常量或表達式。在存儲過程中,參數變量只能被賦值而不能將其用于賦值,在存儲過程中必須給輸出變量至少賦值一次。
4.3 IN OUT參數
語法:參數名 IN OUT 數據類型 DEFAULT 值;
定義一個輸入、輸出參數變量,兼有以上兩者的功能。在調用存儲過程時,主程序的實際參數只能是一個變量,而不能是常量或表達式。DEFAULT 關鍵字為可選項,用來設定參數的默認值。在存儲過程中,變量接收主程序傳遞的值,同時可以參加賦值運算,也可以對其進行賦值。在存儲過程中必須給變量至少賦值一次。
示例:
create or replace procedure say_hi(to_whom in varchar2 default '張三', who out varchar2)
as
who_name varchar(20);
begin
who_name := '李四';
who := who_name;
dbms_output.put_line('Say Hi to '||to_whom);
end;
create or replace procedure invoke_say_hi
as
who varchar2(20);
whom varchar2(20);
begin
whom := '小明';
say_hi(whom, who);
say_hi(who => who);
dbms_output.put_line(who||'say hi to '||whom);
end;
參數的值由調用者傳遞,傳遞的參數的個數、類型和順序應該和定義的一致。如果順序不一致,可以采用以下調用方法:
過程名(參數名 => 參數的值,參數名 => 參數的值, ~~~~~);
如上面的示例: say_hi(who => who); =>運算符左側是參數名,右側是參數表達式.
存儲過程參數寬度:
無法在存儲過程的定義中指定存儲參數的寬度,也就導致了我們無法在存儲過程中控制傳入變量的寬度。這個寬度是完全由外部傳入時決定的。
5.游標
5.1 普通游標
游標定義: cursor [游標名] is [sql語句]
普通游標把整個查詢已經寫死,調用時不可以作任何改變
注意:這里的is不能用as代替
示例:
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | create or replace procedure cursor_sample ? as ? user_name varchar2(20); ? user_id varchar2(20); ? cursor select_user is select t.user_name, t.user_id from t_user t; ? begin ? open select_user; ? loop ? fetch select_user into user_name, user_id; ? exit when select_user%notfound; ? dbms_output.put_line('UserId:'||user_id||' UserName: '||user_name); ? end loop; ? close select_user; ? end; |
5.2動態查詢游標
動態查詢游標,查詢條件的參數由變量決定。
例:
create or replace procedure cursor_sample2
as
vuser_id varchar2(20);
vuser_name varchar2(20);
cursor get_user_by_id is select t.user_id, t.user_name from t_user t where t.user_id = vuser_id;
begin
vuser_id := 'root2';
open get_user_by_id;
loop
fetch get_user_by_id into vuser_id, vuser_name;
exit when get_user_by_id%notfound;
dbms_output.put_line('UserName:'||vuser_name||' UserId:'||vuser_id);
end loop;
close get_user_by_id;
end;
5.3游標變量
先定義了一個引用游標類型,然后再聲明了一個游標變量. 然后再用open for 來打開一個查詢。需要注意的是它可以多次使用,用來打開不同的查詢。
示例:
create or replace procedure cursor_sample3
as
vuser_name varchar2(20);
vuser_id varchar2(20);
type cursor_type is ref cursor;
select_user cursor_type;
begin
open select_user for select t.user_name, t.user_id from t_user t;
loop
fetch select_user into vuser_name, vuser_id;
exit when select_user%notfound;
dbms_output.put_line('UserName:'||vuser_name||' ||UserId:'||vuser_id);
end loop;
close select_user;
dbms_output.put_line('***********************************************');
dbms_output.put_line('****************第二次使用游標**********************');
open select_user for select t.user_name, t.user_id from t_user t where t.user_id = 'root2';
loop
fetch select_user into vuser_name, vuser_id;
exit when select_user%notfound;
dbms_output.put_line('UserName:'||vuser_name||' ||UserId:'||vuser_id);
end loop;
close select_user;
end;
5.4游標循環的方法
(1)游標的%found和%notfound屬性。
能從游標中取出記錄,得到的結果為%found,取不到記錄為%notfound。在打開一個游標之后,馬上檢查它的%found或%notfound屬性,它得到的結果即不是true也不是false.而是null.必須執行一條fetch語句后,這些屬性才有值。
三種循環方法
5.4.1【Loop循環】
loop
fetch select_user into vuser_name, vuser_id;
exit when select_user%notfound;
【Do something】;
end loop;
close select_user;
5.4.2【while循環】
fetch select_user into vuser_name, vuser_id;
while select_user%found loop
dbms_output.put_line('UserName:'||vuser_name||' ||UserId:'||vuser_id);
fetch select_user into vuser_name, vuser_id;
end loop;
close select_user;
說明:我們知道了一個游標打開后,必須執行一次fetch語句,游標的屬性才會起作用。所以使用while 循環時,就需要在循環之前進行一次fetch動作。
而且數據處理動作必須放在循環體內的fetch方法之前。循環體內的fetch方法要放在最后。否則就會多處理一次。這一點也要非常的小心。
使用while來循環處理游標是最復雜的方法。
5.4.3【for循環】
create or replace procedure cursor_sample4
as
vuser_name varchar2(20);
vuser_id varchar2(20);
cursor select_user is select t.user_name, t.user_id from t_user t;
begin
for v_pos in select_user loop
vuser_name := v_pos.user_name;
vuser_id := v_pos.user_id;
dbms_output.put_line('UserName:'||vuser_name||'UserId'||vuser_id);
end loop;
end;
說明:for循環是比較簡單實用的方法。
首先,它會自動open和close游標。解決了你忘記打開或關閉游標的煩惱。
其次,自動定義了一個記錄類型及聲明該類型的變量,并自動fetch數據到這個變量中。 我們需要注意v_pos 這個變量無需要在循環外進行聲明,無需要為其指定數據類型。 它應該是一個記錄類型,具體的結構是由游標決定的。這個變量的作用域僅僅是在循環體內。 把v_pos看作一個記錄變量就可以了,如果要獲得某一個值就像調用記錄一樣就可以了。 如v_pos.user_name由此可見,for循環是用來循環游標的最好方法。高效,簡潔,安全。
6.異常處理
語法:exception when [異常名] then [dosomething] …
When others then [dosomething];
示例:
create or replace procedure exception_sample
as
vuser_name varchar2(20);
vuser_id varchar2(20);
vsqlcode varchar2(10);
vsqlerrm varchar2(1000);
begin
select t.user_name, t.user_id into vuser_name, vuser_id from t_user t where 1 = 0;
exception
when others
then
vsqlcode := sqlcode;
vsqlerrm := sqlerrm;
dbms_output.put_line('Exception, code:'||vsqlcode||' Error message:'||sqlerrm);
end;
另外:若在代碼中需要拋出異常時,用raise+異常名
如以下例子:
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | create or replace procedure exception_sample2 ? as ? vuser_name varchar2(20); ? vuser_id varchar2(20); ? yourexception exception; --自定義異常 ? cursor select_user is select t.user_name, t.user_id from t_user t; ? begin ? open select_user; ? loop ? fetch select_user into vuser_name, vuser_id; ? exit when select_user%notfound; ? if vuser_id = 'root2' then ? raise yourexception; --拋出異常 ? end if; ? end loop; ? exception ? when yourexception then ? dbms_output.put_line('人品異常'); ? when others then ? dbms_output.put_line(sqlcode||sqlerrm); ? end; |
已經命名的異常
命名的系統異常 產生原因
ACCESS_INTO_NULL 未定義對象
CASE_NOT_FOUND CASE 中若未包含相應的 WHEN ,并且沒有設置
ELSE 時
COLLECTION_IS_NULL 集合元素未初始化
CURSER_ALREADY_OPEN 游標已經打開
DUP_VAL_ON_INDEX 唯一索引對應的列上有重復的值
INVALID_CURSOR 在不合法的游標上進行操作
INVALID_NUMBER 內嵌的 SQL 語句不能將字符轉換為數字
NO_DATA_FOUND 使用 select into 未返回行,或應用索引表未初始化的
TOO_MANY_ROWS 執行 select into 時,結果集超過一行
ZERO_DIVIDE 除數為 0
SUBSCRIPT_BEYOND_COUNT 元素下標超過嵌套表或 VARRAY 的最大值
SUBSCRIPT_OUTSIDE_LIMIT 使用嵌套表或 VARRAY 時,將下標指定為負數
VALUE_ERROR 賦值時,變量長度不足以容納實際數據
LOGIN_DENIED PL/SQL 應用程序連接到 oracle 數據庫時,提供了不
正確的用戶名或密碼
NOT_LOGGED_ON PL/SQL 應用程序在沒有連接 oralce 數據庫的情況下
訪問數據
PROGRAM_ERROR PL/SQL 內部問題,可能需要重裝數據字典& pl./SQL
系統包
ROWTYPE_MISMATCH 宿主游標變量與 PL/SQL 游標變量的返回類型不兼容
SELF_IS_NULL 使用對象類型時,在 null 對象上調用對象方法
STORAGE_ERROR 運行 PL/SQL 時,超出內存空間
SYS_INVALID_ID 無效的 ROWID 字符串
TIMEOUT_ON_RESOURCE Oracle 在等待資源時超時
7.過程內部塊
我們知道了存儲過程的結構,語句塊由begin開始,以end結束。這些塊是可以嵌套。在語句塊中可以嵌套任何以下的塊:
Declare … begin … exception … end;
例:
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | create or replace procedure innerblock_sample ? as ? vuser_name varchar2(20); ? vuser_id varchar2(20); ? cursor select_user is select t.user_name, t.user_id from t_user t; ? begin ? open select_user; ? loop ? fetch select_user into vuser_name, vuser_id; ? exit when select_user%notfound; ? dbms_output.put_line('UserName:'||vuser_name||'UserId'||vuser_id); ? end loop; ? close select_user; ? declare ? vuser_level varchar2(20); ? cursor get_level is select t.user_level from t_user t; ? begin ? open get_level; ? loop ? fetch get_level into vuser_level; ? exit when get_level%notfound; ? dbms_output.put_line('UserLevel:'||vuser_level); ? end loop; ? close get_level; ? exception when others then ? dbms_output.put_line(sqlcode||sqlerrm); ? end; ? exception when others then ? dbms_output.put_line(sqlcode||sqlerrm); ? end; |
總結
以上是生活随笔為你收集整理的Oracle存储过程快速入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言再学习 -- dmesg 命令
- 下一篇: C语言再学习 -- grep 命令(转)